Merge branch 'master' into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Fri, 12 Nov 2010 15:15:29 +0000 (16:15 +0100)
committerGuus Sliepen <guus@tinc-vpn.org>
Fri, 12 Nov 2010 15:15:29 +0000 (16:15 +0100)
Conflicts:
doc/tincd.8.in
lib/pidfile.c
src/graph.c
src/net.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/netutl.c
src/node.c
src/node.h
src/protocol_auth.c
src/protocol_key.c
src/tincd.c

28 files changed:
1  2 
doc/tinc.texi
doc/tincd.8.in
have.h
src/bsd/device.c
src/conf.c
src/conf.h
src/connection.c
src/connection.h
src/cygwin/device.c
src/getopt.c
src/linux/device.c
src/logger.c
src/memcmp.c
src/mingw/device.c
src/net.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/process.c
src/process.h
src/protocol_auth.c
src/protocol_edge.c
src/protocol_key.c
src/raw_socket/device.c
src/solaris/device.c
src/tincd.c
src/uml_socket/device.c

diff --combined doc/tinc.texi
index a5ef8e6c4812c1be05d188839ef3b5eb76663bf5,9e0f978da942b4aaf1bfd59bd474a069c438481e..3dc9eca46d899ca1aab790fdb587c5134fcbfab8
@@@ -37,7 -37,6 +37,7 @@@ permission notice identical to this one
  
  @page
  @vskip 0pt plus 1filll
 +@cindex copyright
  This is the info manual for @value{PACKAGE} version @value{VERSION}, a Virtual Private Network daemon.
  
  Copyright @copyright{} 1998-2010 Ivo Timmermans,
@@@ -55,7 -54,7 +55,7 @@@ permission notice identical to this one
  
  @end titlepage
  
 -@ifnottex
 +@ifinfo
  @c ==================================================================
  @node Top
  @top Top
  * Installation::
  * Configuration::
  * Running tinc::
 +* Controlling tinc::
  * Technical information::
  * Platform specific information::
  * About us::
  * Concept Index::               All used terms explained
  @end menu
 -@end ifnottex
 +@end ifinfo
  
  @c ==================================================================
  @node    Introduction
@@@ -339,7 -337,6 +339,7 @@@ having them installed, configure will g
  * OpenSSL::
  * zlib::
  * lzo::
 +* libevent::
  @end menu
  
  
@@@ -452,27 -449,6 +452,27 @@@ make sure you build development and run
  default).
  
  
 +@c ==================================================================
 +@node       libevent
 +@subsection libevent
 +
 +@cindex libevent
 +For the main event loop, tinc uses the libevent library.
 +
 +If this library is not installed, you wil get an error when configuring
 +tinc for build.
 +
 +You can use your operating system's package manager to install this if
 +available.  Make sure you install the development AND runtime versions
 +of this package.
 +
 +If you have to install libevent manually, you can get the source code
 +from @url{http://monkey.org/~provos/libevent/}.  Instructions on how to configure,
 +build and install this package are included within the package.  Please
 +make sure you build development and runtime libraries (which is the
 +default).
 +
 +
  @c
  @c
  @c
@@@ -749,6 -725,13 +749,13 @@@ and carriage returns are ignored.  Note
  in the `=' sign, but doing so improves readability.  If you leave it
  out, remember to replace it with at least one space character.
  
+ The server configuration is complemented with host specific configuration (see
+ the next section). Although all host configuration options for the local node
+ listed in this document can also be put in
+ @file{@value{sysconfdir}/tinc/@var{netname}/tinc.conf}, it is recommended to
+ put host specific configuration options in the host configuration file, as this
+ makes it easy to exchange with other nodes.
  In this section all valid variables are listed in alphabetical order.
  The default value is given between parentheses,
  other comments are between square brackets.
@@@ -967,7 -950,7 +974,7 @@@ accidental eavesdropping if you are edi
  @cindex PrivateKeyFile
  @item PrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/rsa_key.priv})
  This is the full path name of the RSA private key file that was
 -generated by @samp{tincd --generate-keys}.  It must be a full path, not a
 +generated by @samp{tincctl generate-keys}.  It must be a full path, not a
  relative directory.
  
  Note that there must be exactly one of PrivateKey
@@@ -1067,7 -1050,7 +1074,7 @@@ This is the RSA public key for this hos
  @cindex PublicKeyFile
  @item PublicKeyFile = <@var{path}> [obsolete]
  This is the full path name of the RSA public key file that was generated
 -by @samp{tincd --generate-keys}.  It must be a full path, not a relative
 +by @samp{tincctl generate-keys}.  It must be a full path, not a relative
  directory.
  
  @cindex PEM format
@@@ -1103,6 -1086,7 +1110,6 @@@ example: netmask 255.255.255.0 would be
  /22. This conforms to standard CIDR notation as described in
  @uref{ftp://ftp.isi.edu/in-notes/rfc1519.txt, RFC1519}
  
 -@cindex Subnet weight
  A Subnet can be given a weight to indicate its priority over identical Subnets
  owned by different nodes. The default weight is 10. Lower values indicate
  higher priority. Packets will be sent to the node with the highest priority,
@@@ -1110,12 -1094,15 +1117,12 @@@ unless that node is not reachable, in w
  priority will be tried, and so on.
  
  @cindex TCPonly
 -@item TCPonly = <yes|no> (no) [deprecated]
 +@item TCPonly = <yes|no> (no)
  If this variable is set to yes, then the packets are tunnelled over a
  TCP connection instead of a UDP connection.  This is especially useful
  for those who want to run a tinc daemon from behind a masquerading
  firewall, or if UDP packet routing is disabled somehow.
  Setting this options also implicitly sets IndirectData.
 -
 -Since version 1.0.10, tinc will automatically detect whether communication via
 -UDP is possible or not.
  @end table
  
  
@@@ -1204,6 -1191,10 +1211,6 @@@ this is set to the port number it uses 
  @item SUBNET
  When a subnet becomes (un)reachable, this is set to the subnet.
  
 -@cindex WEIGHT
 -@item WEIGHT
 -When a subnet becomes (un)reachable, this is set to the subnet weight.
 -
  @end table
  
  
@@@ -1250,7 -1241,7 +1257,7 @@@ Now that you have already created the m
  you can easily create a public/private keypair by entering the following command:
  
  @example
 -tincd -n @var{netname} -K
 +tincctl -n @var{netname} generate-keys
  @end example
  
  Tinc will generate a public and a private key and ask you where to put them.
@@@ -1479,7 -1470,7 +1486,7 @@@ Address = 4.5.6.
  A, B, C and D all have generated a public/private keypair with the following command:
  
  @example
 -tincd -n company -K
 +tincctl -n company generate-keys
  @end example
  
  The private key is stored in @file{@value{sysconfdir}/tinc/company/rsa_key.priv},
@@@ -1545,12 -1536,20 +1552,12 @@@ This will also disable the automatic re
  Set debug level to @var{level}.  The higher the debug level, the more gets
  logged.  Everything goes via syslog.
  
 -@item -k, --kill[=@var{signal}]
 -Attempt to kill a running tincd (optionally with the specified @var{signal} instead of SIGTERM) and exit.
 -Use it in conjunction with the -n option to make sure you kill the right tinc daemon.
 -Under native Windows the optional argument is ignored,
 -the service will always be stopped and removed.
 -
  @item -n, --net=@var{netname}
  Use configuration for net @var{netname}. @xref{Multiple networks}.
  
 -@item -K, --generate-keys[=@var{bits}]
 -Generate public/private keypair of @var{bits} length. If @var{bits} is not specified,
 -2048 is the default. tinc will ask where you want to store the files,
 -but will default to the configuration directory (you can use the -c or -n option
 -in combination with -K). After that, tinc will quit.
 +@item --controlsocket=@var{filename}
 +Open control socket at @var{filename}. If unspecified, the default is
 +@file{@value{localstatedir}/run/tinc.@var{netname}.control}.
  
  @item -L, --mlock
  Lock tinc into main memory.
@@@ -1560,6 -1559,9 +1567,6 @@@ This will prevent sensitive data like s
  Write log entries to a file instead of to the system logging facility.
  If @var{file} is omitted, the default is @file{@value{localstatedir}/log/tinc.@var{netname}.log}.
  
 -@item --pidfile=@var{file}
 -Write PID to @var{file} instead of @file{@value{localstatedir}/run/tinc.@var{netname}.pid}.
 -
  @item --bypass-security
  Disables encryption and authentication.
  Only useful for debugging.
@@@ -1599,11 -1601,31 +1606,11 @@@ You can also send the following signal
  @c from the manpage
  @table @samp
  
 -@item ALRM
 -Forces tinc to try to connect to all uplinks immediately.
 -Usually tinc attempts to do this itself,
 -but increases the time it waits between the attempts each time it failed,
 -and if tinc didn't succeed to connect to an uplink the first time after it started,
 -it defaults to the maximum time of 15 minutes.
 -
  @item HUP
  Partially rereads configuration files.
  Connections to hosts whose host config file are removed are closed.
  New outgoing connections specified in @file{tinc.conf} will be made.
  
 -@item INT
 -Temporarily increases debug level to 5.
 -Send this signal again to revert to the original level.
 -
 -@item USR1
 -Dumps the connection list to syslog.
 -
 -@item USR2
 -Dumps virtual network device statistics, all known nodes, edges and subnets to syslog.
 -
 -@item WINCH
 -Purges all information remembered about unreachable nodes.
 -
  @end table
  
  @c ==================================================================
@@@ -1667,7 -1689,7 +1674,7 @@@ Do you have a firewall or a NAT device 
  If so, check that it allows TCP and UDP traffic on port 655.
  If it masquerades and the host running tinc is behind it, make sure that it forwards TCP and UDP traffic to port 655 to the host running tinc.
  You can add @samp{TCPOnly = yes} to your host config file to force tinc to only use a single TCP connection,
 -this works through most firewalls and NATs. Since version 1.0.10, tinc will automatically fall back to TCP if direct communication via UDP is not possible.
 +this works through most firewalls and NATs.
  
  @end itemize
  
@@@ -1766,8 -1788,6 +1773,8 @@@ or if that is not the case, try changin
  
  @itemize
  @item If you see this only sporadically, it is harmless and caused by a node sending packets using an old key.
 +@item If you see this often and another node is not reachable anymore, then a NAT (masquerading firewall) is changing the source address of UDP packets.
 +You can add @samp{TCPOnly = yes} to host configuration files to force all VPN traffic to go over a TCP connection.
  @end itemize
  
  @item Got bad/bogus/unauthorized REQUEST from foo (1.2.3.4 port 12345)
@@@ -1798,110 -1818,6 +1805,110 @@@ Be sure to include the following inform
  @item The output of any command that fails to work as it should (like ping or traceroute).
  @end itemize
  
 +@c ==================================================================
 +@node    Controlling tinc
 +@chapter Controlling tinc
 +
 +You can control and inspect a running @samp{tincd} through the @samp{tincctl}
 +command. A quick example:
 +
 +@example
 +tincctl -n @var{netname} reload
 +@end example
 +
 +@menu
 +* tincctl runtime options::
 +* tincctl commands::
 +@end menu
 +
 +
 +@c ==================================================================
 +@node    tincctl runtime options
 +@section tincctl runtime options
 +
 +@c from the manpage
 +@table @option
 +@item -c, --config=@var{path}
 +Read configuration options from the directory @var{path}.  The default is
 +@file{@value{sysconfdir}/tinc/@var{netname}/}.
 +
 +@item -n, --net=@var{netname}
 +Use configuration for net @var{netname}. @xref{Multiple networks}.
 +
 +@item --controlsocket=@var{filename}
 +Open control socket at @var{filename}. If unspecified, the default is
 +@file{@value{localstatedir}/run/tinc.@var{netname}.control}.
 +
 +@item --help
 +Display a short reminder of runtime options and commands, then terminate.
 +
 +@item --version
 +Output version information and exit.
 +
 +@end table
 +
 +
 +@c ==================================================================
 +@node    tincctl commands
 +@section tincctl commands
 +
 +@c from the manpage
 +@table @code
 +
 +@item start
 +Start @samp{tincd}.
 +
 +@item stop
 +Stop @samp{tincd}.
 +
 +@item restart
 +Restart @samp{tincd}.
 +
 +@item reload
 +Partially rereads configuration files. Connections to hosts whose host
 +config files are removed are closed. New outgoing connections specified
 +in @file{tinc.conf} will be made.
 +
 +@item pid
 +Shows the PID of the currently running @samp{tincd}.
 +
 +@item generate-keys [@var{bits}]
 +Generate public/private keypair of @var{bits} length. If @var{bits} is not specified,
 +1024 is the default. tinc will ask where you want to store the files,
 +but will default to the configuration directory (you can use the -c or -n
 +option).
 +
 +@item dump nodes
 +Dump a list of all known nodes in the VPN.
 +
 +@item dump edges
 +Dump a list of all known connections in the VPN.
 +
 +@item dump subnets
 +Dump a list of all known subnets in the VPN.
 +
 +@item dump connections
 +Dump a list of all meta connections with ourself.
 +
 +@item dump graph
 +Dump a graph of the VPN in dotty format.
 +
 +@item purge
 +Purges all information remembered about unreachable nodes.
 +
 +@item debug @var{level}
 +Sets debug level to @var{level}.
 +
 +@item retry
 +Forces tinc to try to connect to all uplinks immediately.
 +Usually tinc attempts to do this itself,
 +but increases the time it waits between the attempts each time it failed,
 +and if tinc didn't succeed to connect to an uplink the first time after it started,
 +it defaults to the maximum time of 15 minutes.
 +
 +@end table
 +
 +
  @c ==================================================================
  @node    Technical information
  @chapter Technical information
diff --combined doc/tincd.8.in
index 9995b4c746fb0ac4c4a686a7cc130e87a578aaf7,055863029bc2d3b7b62d5bf75e13acefa6e432ac..df6af144fc6b50bdab577bce05a3c40f5918216d
@@@ -8,13 -8,16 +8,13 @@@
  .Nd tinc VPN daemon
  .Sh SYNOPSIS
  .Nm
 -.Op Fl cdDkKnLRU
 +.Op Fl cdDKnLRU
  .Op Fl -config Ns = Ns Ar DIR
  .Op Fl -no-detach
  .Op Fl -debug Ns Op = Ns Ar LEVEL
 -.Op Fl -kill Ns Op = Ns Ar SIGNAL
  .Op Fl -net Ns = Ns Ar NETNAME
 -.Op Fl -generate-keys Ns Op = Ns Ar BITS
  .Op Fl -mlock
  .Op Fl -logfile Ns Op = Ns Ar FILE
 -.Op Fl -pidfile Ns = Ns Ar FILE
  .Op Fl -bypass-security
  .Op Fl -chroot
  .Op Fl -user Ns = Ns Ar USER
@@@ -50,9 -53,24 +50,9 @@@ If not mentioned otherwise, this will s
  Increase debug level or set it to
  .Ar LEVEL
  (see below).
 -.It Fl k, -kill Ns Op = Ns Ar SIGNAL
 -Attempt to kill a running
 -.Nm
 -(optionally with the specified
 -.Ar SIGNAL
 -instead of SIGTERM) and exit.
 -Under Windows (not Cygwin) the optional argument is ignored,
 -the service will always be stopped and removed.
  .It Fl n, -net Ns = Ns Ar NETNAME
  Connect to net
  .Ar NETNAME .
 -.It Fl K, -generate-keys Ns Op = Ns Ar BITS
 -Generate public/private RSA keypair and exit.
 -If
 -.Ar BITS
 -is omitted, the default length will be 2048 bits.
 -When saving keys to existing files, tinc will not delete the old keys,
 -you have to remove them manually.
  .It Fl L, -mlock
  Lock tinc into main memory.
  This will prevent sensitive data like shared private keys to be written to the system swap files/partitions.
@@@ -62,21 -80,20 +62,21 @@@ I
  .Ar FILE
  is omitted, the default is
  .Pa @localstatedir@/log/tinc. Ns Ar NETNAME Ns Pa .log.
 -.It Fl -pidfile Ns = Ns Ar FILE
 -Write PID to
 +.It Fl -controlsocket Ns = Ns Ar FILENAME
 +Open control socket at
 +.Ar FILENAME .
 +If
  .Ar FILE
 -instead of
 -.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .pid.
 -Under Windows this option will be ignored.
 +is omitted, the default is
 +.Pa @localstatedir@/run/tinc. Ns Ar NETNAME Ns Pa .control.
  .It Fl -bypass-security
  Disables encryption and authentication of the meta protocol.
  Only useful for debugging.
- .It Fl -chroot
+ .It Fl R, -chroot
  With this option tinc chroots into the directory where network
  config is located (@sysconfdir@/tinc/NETNAME if -n option is used,
  or to the directory specified with -c option) after initialization.
- .It Fl -user Ns = Ns Ar USER
+ .It Fl U, -user Ns = Ns Ar USER
  setuid to the specified
  .Ar USER
  after initialization.
@@@ -87,12 -104,33 +87,12 @@@ Output version information and exit
  .El
  .Sh SIGNALS
  .Bl -tag -width indent
 -.It ALRM
 -Forces
 -.Nm
 -to try to connect to all uplinks immediately.
 -Usually
 -.Nm
 -attempts to do this itself,
 -but increases the time it waits between the attempts each time it failed,
 -and if
 -.Nm
 -didn't succeed to connect to an uplink the first time after it started,
 -it defaults to the maximum time of 15 minutes.
  .It HUP
  Partially rereads configuration files.
  Connections to hosts whose host config file are removed are closed.
  New outgoing connections specified in
  .Pa tinc.conf
  will be made.
 -.It INT
 -Temporarily increases debug level to 5.
 -Send this signal again to revert to the original level.
 -.It USR1
 -Dumps the connection list to syslog.
 -.It USR2
 -Dumps virtual network device statistics, all known nodes, edges and subnets to syslog.
 -.It WINCH
 -Purges all information remembered about unreachable nodes.
  .El
  .Sh DEBUG LEVELS
  The tinc daemon can send a lot of messages to the syslog.
@@@ -139,7 -177,6 +139,7 @@@ If you find any bugs, report them to ti
  .Sh TODO
  A lot, especially security auditing.
  .Sh SEE ALSO
 +.Xr tincctl 8 ,
  .Xr tinc.conf 5 ,
  .Pa http://www.tinc-vpn.org/ ,
  .Pa http://www.cabal.org/ .
diff --combined have.h
index 89454feba1e92cf46db611f3d3271b42ebb45002,923e76ab3e0f9a96e6fb047c287585b5df5b169f..21c16efa8be933233e9c1666521fa3a0b8057e57
--- 1/have.h
--- 2/have.h
+++ b/have.h
  #ifndef __TINC_HAVE_H__
  #define __TINC_HAVE_H__
  
+ #ifdef HAVE_MINGW
+ #ifdef WITH_WINDOWS2000
+ #define WINVER Windows2000
+ #else
+ #define WINVER WindowsXP
+ #endif
++#define WIN32_LEAN_AND_MEAN
+ #endif
  #include <stdio.h>
  #include <stdlib.h>
  #include <stdarg.h>
  #include <unistd.h>
  
  #ifdef HAVE_MINGW
- #ifdef WITH_WINDOWS2000
- #define WINVER Windows2000
- #else
- #define WINVER WindowsXP
- #endif
- #define WIN32_LEAN_AND_MEAN
  #include <w32api.h>
  #include <windows.h>
  #include <ws2tcpip.h>
  #include <sys/time.h>
  #endif
  
 +#ifdef HAVE_TIME_H
 +#include <time.h>
 +#endif
 +
  #ifdef HAVE_SYS_TYPES_H
  #include <sys/types.h>
  #endif
  #include <sys/uio.h>
  #endif
  
 +#ifdef HAVE_SYS_UN_H
 +#include <sys/un.h>
 +#endif
 +
  #ifdef HAVE_DIRENT_H
  #include <dirent.h>
  #endif
  #include <netinet/if_ether.h>
  #endif
  
 +#ifdef HAVE_EVENT_H
 +#include <event.h>
 +#endif
 +
  #endif /* __TINC_SYSTEM_H__ */
diff --combined src/bsd/device.c
index a9e39d4a4239f3503999a201f181706ec51194a1,e8fdc1bc3abc94b603f022f8382c415d39babb96..993b9825287cc975a529633b0d36c5b6bf6544d5
@@@ -47,8 -47,8 +47,8 @@@ int device_fd = -1
  char *device = NULL;
  char *iface = NULL;
  static char *device_info = NULL;
- static int device_total_in = 0;
- static int device_total_out = 0;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
  #if defined(TUNEMU)
  static device_type_t device_type = DEVICE_TYPE_TUNEMU;
  #elif defined(HAVE_OPENBSD) || defined(HAVE_FREEBSD)
@@@ -64,7 -64,7 +64,7 @@@ bool setup_device(void) 
                device = xstrdup(DEFAULT_DEVICE);
  
        if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
-               iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device);
+               iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
  
        if(get_config_string(lookup_config(config_tree, "DeviceType"), &type)) {
                if(!strcasecmp(type, "tun"))
@@@ -190,20 -190,20 +190,20 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
        switch(device_type) {
                case DEVICE_TYPE_TUN:
  #ifdef HAVE_TUNEMU
                case DEVICE_TYPE_TUNEMU:
                        if(device_type == DEVICE_TYPE_TUNEMU)
 -                              lenin = tunemu_read(device_fd, packet->data + 14, MTU - 14);
 +                              inlen = tunemu_read(device_fd, packet->data + 14, MTU - 14);
                        else
  #else
 -                              lenin = read(device_fd, packet->data + 14, MTU - 14);
 +                              inlen = read(device_fd, packet->data + 14, MTU - 14);
  #endif
  
 -                      if(lenin <= 0) {
 +                      if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
                                        return false;
                        }
  
 -                      packet->len = lenin + 14;
 +                      packet->len = inlen + 14;
                        break;
  
                case DEVICE_TYPE_TUNIFHEAD: {
                        u_int32_t type;
 -                      struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, MTU - 14}};
 +                      struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, MTU - 14}};
  
 -                      if((lenin = readv(device_fd, vector, 2)) <= 0) {
 +                      if((inlen = readv(device_fd, vector, 2)) <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
                                        return false;
                        }
  
 -                      packet->len = lenin + 10;
 +                      packet->len = inlen + 10;
                        break;
                }
  
                case DEVICE_TYPE_TAP:
 -                      if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
 +                      if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                return false;
                        }
  
 -                      packet->len = lenin;
 +                      packet->len = inlen;
                        break;
  
                default:
@@@ -297,7 -297,7 +297,7 @@@ bool write_packet(vpn_packet_t *packet
  
                case DEVICE_TYPE_TUNIFHEAD: {
                        u_int32_t type;
 -                      struct iovec vector[2] = {{&type, sizeof(type)}, {packet->data + 14, packet->len - 14}};
 +                      struct iovec vector[2] = {{&type, sizeof type}, {packet->data + 14, packet->len - 14}};
                        int af;
                        
                        af = (packet->data[12] << 8) + packet->data[13];
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10d", device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10d", device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  }
diff --combined src/conf.c
index d5bc9165804209608d30ced5bf589128e3b946ce,0727953127e141ac633fc1e8e79bbaa911ab4b95..faff003b260807b258701aa656119eb25d882a3b
@@@ -2,7 -2,8 +2,8 @@@
      conf.c -- configuration code
      Copyright (C) 1998 Robert van der Meulen
                    1998-2005 Ivo Timmermans
-                   2000-2009 Guus Sliepen <guus@tinc-vpn.org>
+                   2000-2010 Guus Sliepen <guus@tinc-vpn.org>
+                   2010 Julien Muchembled <jm@jmuchemb.eu>
                  2000 Cris van Pelt
  
      This program is free software; you can redistribute it and/or modify
@@@ -22,7 -23,8 +23,8 @@@
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
+ #include "connection.h"
  #include "conf.h"
  #include "logger.h"
  #include "netutl.h"                           /* for str2address */
  #include "utils.h"                            /* for cp */
  #include "xalloc.h"
  
 -avl_tree_t *config_tree;
 +splay_tree_t *config_tree;
  
  int pinginterval = 0;                 /* seconds between pings */
  int pingtimeout = 0;                  /* seconds to wait for response */
  char *confbase = NULL;                        /* directory in which all config files are */
  char *netname = NULL;                 /* name of the vpn network */
+ list_t *cmdline_conf = NULL;  /* global/host configuration values given at the command line */
  
  static int config_compare(const config_t *a, const config_t *b) {
        int result;
        if(result)
                return result;
  
+       /* give priority to command line options */
+       result = !b->file - !a->file;
+       if (result)
+               return result;
        result = a->line - b->line;
  
        if(result)
                return result;
        else
-               return strcmp(a->file, b->file);
+               return a->file ? strcmp(a->file, b->file) : 0;
  }
  
 -void init_configuration(avl_tree_t ** config_tree) {
 -      *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
 +void init_configuration(splay_tree_t ** config_tree) {
 +      *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
  }
  
 -void exit_configuration(avl_tree_t ** config_tree) {
 -      avl_delete_tree(*config_tree);
 +void exit_configuration(splay_tree_t ** config_tree) {
 +      splay_delete_tree(*config_tree);
        *config_tree = NULL;
  }
  
@@@ -79,18 -88,18 +88,18 @@@ void free_config(config_t *cfg) 
        free(cfg);
  }
  
 -void config_add(avl_tree_t *config_tree, config_t *cfg) {
 -      avl_insert(config_tree, cfg);
 +void config_add(splay_tree_t *config_tree, config_t *cfg) {
 +      splay_insert(config_tree, cfg);
  }
  
 -config_t *lookup_config(avl_tree_t *config_tree, char *variable) {
 +config_t *lookup_config(splay_tree_t *config_tree, char *variable) {
        config_t cfg, *found;
  
        cfg.variable = variable;
-       cfg.file = "";
+       cfg.file = NULL;
        cfg.line = 0;
  
 -      found = avl_search_closest_greater(config_tree, &cfg);
 +      found = splay_search_closest_greater(config_tree, &cfg);
  
        if(!found)
                return NULL;
        return found;
  }
  
 -config_t *lookup_config_next(avl_tree_t *config_tree, const config_t *cfg) {
 -      avl_node_t *node;
 +config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) {
 +      splay_node_t *node;
        config_t *found;
  
 -      node = avl_search_node(config_tree, cfg);
 +      node = splay_search_node(config_tree, cfg);
  
        if(node) {
                if(node->next) {
@@@ -193,9 -202,9 +202,9 @@@ bool get_config_subnet(const config_t *
        /* Teach newbies what subnets are... */
  
        if(((subnet.type == SUBNET_IPV4)
 -              && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t)))
 +              && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address))
                || ((subnet.type == SUBNET_IPV6)
 -              && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) {
 +              && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) {
                logger(LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
                           cfg->variable, cfg->file, cfg->line);
                return false;
@@@ -233,17 -242,54 +242,54 @@@ static char *readline(FILE * fp, char *
        return buf;
  }
  
+ config_t *parse_config_line(char *line, const char *fname, int lineno) {
+       config_t *cfg;
+       int len;
+       char *variable, *value, *eol;
+       variable = value = line;
+       eol = line + strlen(line);
+       while(strchr("\t ", *--eol))
+               *eol = '\0';
+       len = strcspn(value, "\t =");
+       value += len;
+       value += strspn(value, "\t ");
+       if(*value == '=') {
+               value++;
+               value += strspn(value, "\t ");
+       }
+       variable[len] = '\0';
+       if(!*value) {
+               const char err[] = "No value for variable";
+               if (fname)
+                       logger(LOG_ERR, "%s `%s' on line %d while reading config file %s",
+                               err, variable, lineno, fname);
+               else
+                       logger(LOG_ERR, "%s `%s' in command line option %d",
+                               err, variable, lineno);
+               return NULL;
+       }
+       cfg = new_config();
+       cfg->variable = xstrdup(variable);
+       cfg->value = xstrdup(value);
+       cfg->file = fname ? xstrdup(fname) : NULL;
+       cfg->line = lineno;
+       return cfg;
+ }
  /*
    Parse a configuration file and put the results in the configuration tree
    starting at *base.
  */
- int read_config_file(splay_tree_t *config_tree, const char *fname) {
 -bool read_config_file(avl_tree_t *config_tree, const char *fname) {
++bool read_config_file(splay_tree_t *config_tree, const char *fname) {
        FILE *fp;
        char buffer[MAX_STRING_SIZE];
        char *line;
-       char *variable, *value, *eol;
        int lineno = 0;
-       int len;
        bool ignore = false;
        config_t *cfg;
        bool result = false;
                        continue;
                }
  
-               variable = value = line;
-               eol = line + strlen(line);
-               while(strchr("\t ", *--eol))
-                       *eol = '\0';
-               len = strcspn(value, "\t =");
-               value += len;
-               value += strspn(value, "\t ");
-               if(*value == '=') {
-                       value++;
-                       value += strspn(value, "\t ");
-               }
-               variable[len] = '\0';
-       
-               if(!*value) {
-                       logger(LOG_ERR, "No value for variable `%s' on line %d while reading config file %s",
-                                  variable, lineno, fname);
+               cfg = parse_config_line(line, fname, lineno);
+               if (!cfg)
                        break;
-               }
-               cfg = new_config();
-               cfg->variable = xstrdup(variable);
-               cfg->value = xstrdup(value);
-               cfg->file = xstrdup(fname);
-               cfg->line = lineno;
                config_add(config_tree, cfg);
        }
  
        return result;
  }
  
 -void read_config_options(avl_tree_t *config_tree, const char *prefix) {
++void read_config_options(splay_tree_t *config_tree, const char *prefix) {
+       list_node_t *node, *next;
+       size_t prefix_len = prefix ? strlen(prefix) : 0;
+       for(node = cmdline_conf->tail; node; node = next) {
+               config_t *cfg = (config_t *)node->data;
+               next = node->prev;
+               if(!prefix && strchr(cfg->variable, '.'))
+                       continue;
+               if(prefix && (strncmp(prefix, cfg->variable, prefix_len) || cfg->variable[prefix_len] != '.'))
+                       continue;
+               config_add(config_tree, cfg);
+               node->data = NULL;
+               list_unlink_node(cmdline_conf, node);
+       }
+ }
  bool read_server_config() {
        char *fname;
        bool x;
  
+       read_config_options(config_tree, NULL);
        xasprintf(&fname, "%s/tinc.conf", confbase);
        x = read_config_file(config_tree, fname);
  
        return x;
  }
  
 -FILE *ask_and_open(const char *filename, const char *what) {
 -      FILE *r;
 -      char *directory;
 -      char line[PATH_MAX];
 -      const char *fn;
 -
 -      /* Check stdin and stdout */
 -      if(!isatty(0) || !isatty(1)) {
 -              /* Argh, they are running us from a script or something.  Write
 -                 the files to the current directory and let them burn in hell
 -                 for ever. */
 -              fn = filename;
 -      } else {
 -              /* Ask for a file and/or directory name. */
 -              fprintf(stdout, "Please enter a file to save %s to [%s]: ",
 -                              what, filename);
 -              fflush(stdout);
 -
 -              fn = readline(stdin, line, sizeof line);
 -
 -              if(!fn) {
 -                      fprintf(stderr, "Error while reading stdin: %s\n",
 -                                      strerror(errno));
 -                      return NULL;
 -              }
 -
 -              if(!strlen(fn))
 -                      /* User just pressed enter. */
 -                      fn = filename;
 -      }
 -
 -#ifdef HAVE_MINGW
 -      if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
 -#else
 -      if(fn[0] != '/') {
 -#endif
 -              /* The directory is a relative path or a filename. */
 -              char *p;
 -
 -              directory = get_current_dir_name();
 -              xasprintf(&p, "%s/%s", directory, fn);
 -              free(directory);
 -              fn = p;
 -      }
 -
 -      umask(0077);                            /* Disallow everything for group and other */
 -
 -      /* Open it first to keep the inode busy */
 -
 -      r = fopen(fn, "r+") ?: fopen(fn, "w+");
 -
 -      if(!r) {
 -              fprintf(stderr, "Error opening file `%s': %s\n",
 -                              fn, strerror(errno));
 -              return NULL;
 -      }
 -
 -      return r;
 -}
 -
+ bool read_connection_config(connection_t *c) {
+       char *fname;
+       bool x;
+       read_config_options(c->config_tree, c->name);
+       xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
+       x = read_config_file(c->config_tree, fname);
+       free(fname);
+       return x;
+ }
  bool disable_old_keys(FILE *f) {
        char buf[100];
        long pos;
        rewind(f);
        pos = ftell(f);
  
+       if(pos < 0)
+               return false;
        while(fgets(buf, sizeof buf, f)) {
                if(!strncmp(buf, "-----BEGIN RSA", 14)) {       
                        buf[11] = 'O';
                        buf[12] = 'L';
                        buf[13] = 'D';
-                       fseek(f, pos, SEEK_SET);
-                       fputs(buf, f);
+                       if(fseek(f, pos, SEEK_SET))
+                               break;
+                       if(fputs(buf, f) <= 0)
+                               break;
                        disabled = true;
                }
                else if(!strncmp(buf, "-----END RSA", 12)) {    
                        buf[ 9] = 'O';
                        buf[10] = 'L';
                        buf[11] = 'D';
-                       fseek(f, pos, SEEK_SET);
-                       fputs(buf, f);
+                       if(fseek(f, pos, SEEK_SET))
+                               break;
+                       if(fputs(buf, f) <= 0)
+                               break;
                        disabled = true;
                }
                pos = ftell(f);
+               if(pos < 0)
+                       break;
        }
  
        return disabled;
diff --combined src/conf.h
index be70c24c32c8ce4d4adeb1dbcde8e8c652e4ad6a,3eae4ad7227e96117280926260436785ec155131..cc57e82b4a11bfc27d949912b88d3025e775fc89
@@@ -21,7 -21,8 +21,8 @@@
  #ifndef __TINC_CONF_H__
  #define __TINC_CONF_H__
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
+ #include "list.h"
  
  typedef struct config_t {
        char *variable;
@@@ -32,7 -33,7 +33,7 @@@
  
  #include "subnet.h"
  
 -extern avl_tree_t *config_tree;
 +extern splay_tree_t *config_tree;
  
  extern int pinginterval;
  extern int pingtimeout;
@@@ -40,23 -41,27 +41,27 @@@ extern int maxtimeout
  extern bool bypass_security;
  extern char *confbase;
  extern char *netname;
+ extern list_t *cmdline_conf;
  
 -extern void init_configuration(avl_tree_t **);
 -extern void exit_configuration(avl_tree_t **);
 +extern void init_configuration(splay_tree_t **);
 +extern void exit_configuration(splay_tree_t **);
  extern config_t *new_config(void) __attribute__ ((__malloc__));
  extern void free_config(config_t *);
 -extern void config_add(avl_tree_t *, config_t *);
 -extern config_t *lookup_config(avl_tree_t *, char *);
 -extern config_t *lookup_config_next(avl_tree_t *, const config_t *);
 +extern void config_add(splay_tree_t *, config_t *);
 +extern config_t *lookup_config(splay_tree_t *, char *);
 +extern config_t *lookup_config_next(splay_tree_t *, const config_t *);
  extern bool get_config_bool(const config_t *, bool *);
  extern bool get_config_int(const config_t *, int *);
  extern bool get_config_string(const config_t *, char **);
  extern bool get_config_address(const config_t *, struct addrinfo **);
  extern bool get_config_subnet(const config_t *, struct subnet_t **);
  
- extern int read_config_file(splay_tree_t *, const char *);
+ extern config_t *parse_config_line(char *, const char *, int);
 -extern bool read_config_file(avl_tree_t *, const char *);
 -extern void read_config_options(avl_tree_t *, const char *);
++extern bool read_config_file(splay_tree_t *, const char *);
++extern void read_config_options(splay_tree_t *, const char *);
  extern bool read_server_config(void);
 -extern FILE *ask_and_open(const char *, const char *);
+ extern bool read_connection_config(struct connection_t *);
 +extern FILE *ask_and_open(const char *, const char *, const char *);
  extern bool is_safe_path(const char *);
  extern bool disable_old_keys(FILE *);
  
diff --combined src/connection.c
index 18c03c7e8c808377e843f10e77b836296b876f36,ac946abe12c53c3c914dfda6b0bbabd593dec339..fecb48d42d141d37a1830e039fd91e8d629736d9
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "conf.h"
 +#include "control_common.h"
  #include "list.h"
  #include "logger.h"
  #include "net.h"                              /* Don't ask. */
@@@ -33,7 -31,7 +33,7 @@@
  #include "utils.h"
  #include "xalloc.h"
  
 -avl_tree_t *connection_tree;  /* Meta connections */
 +splay_tree_t *connection_tree;        /* Meta connections */
  connection_t *broadcast;
  
  static int connection_compare(const connection_t *a, const connection_t *b) {
  }
  
  void init_connections(void) {
 -      connection_tree = avl_alloc_tree((avl_compare_t) connection_compare, (avl_action_t) free_connection);
 +      connection_tree = splay_alloc_tree((splay_compare_t) connection_compare, (splay_action_t) free_connection);
        broadcast = new_connection();
        broadcast->name = xstrdup("everyone");
        broadcast->hostname = xstrdup("BROADCAST");
  }
  
  void exit_connections(void) {
 -      avl_delete_tree(connection_tree);
 +      splay_delete_tree(connection_tree);
        free_connection(broadcast);
  }
  
  connection_t *new_connection(void) {
 -      connection_t *c;
 -
 -      c = xmalloc_and_zero(sizeof(connection_t));
 -
 -      if(!c)
 -              return NULL;
 -
 -      gettimeofday(&c->start, NULL);
 -
 -      return c;
 +      return xmalloc_and_zero(sizeof(connection_t));
  }
  
  void free_connection(connection_t *c) {
 +      if(!c)
 +              return;
 +
        if(c->name)
                free(c->name);
  
        if(c->hostname)
                free(c->hostname);
  
 -      if(c->inkey)
 -              free(c->inkey);
 -
 -      if(c->outkey)
 -              free(c->outkey);
 -
 -      if(c->inctx) {
 -              EVP_CIPHER_CTX_cleanup(c->inctx);
 -              free(c->inctx);
 -      }
 -
 -      if(c->outctx) {
 -              EVP_CIPHER_CTX_cleanup(c->outctx);
 -              free(c->outctx);
 -      }
 -
 -      if(c->mychallenge)
 -              free(c->mychallenge);
 +      cipher_close(&c->incipher);
 +      cipher_close(&c->outcipher);
  
        if(c->hischallenge)
                free(c->hischallenge);
        if(c->config_tree)
                exit_configuration(&c->config_tree);
  
 -      if(c->outbuf)
 -              free(c->outbuf);
 -
 -      if(c->rsa_key)
 -              RSA_free(c->rsa_key);
 +      if(c->buffer)
 +              bufferevent_free(c->buffer);
 +      
 +      if(event_initialized(&c->inevent))
 +              event_del(&c->inevent);
  
        free(c);
  }
  
  void connection_add(connection_t *c) {
 -      avl_insert(connection_tree, c);
 +      splay_insert(connection_tree, c);
  }
  
  void connection_del(connection_t *c) {
 -      avl_delete(connection_tree, c);
 +      splay_delete(connection_tree, c);
  }
  
 -void dump_connections(void) {
 -      avl_node_t *node;
 +bool dump_connections(connection_t *cdump) {
 +      splay_node_t *node;
        connection_t *c;
  
 -      logger(LOG_DEBUG, "Connections:");
 -
        for(node = connection_tree->head; node; node = node->next) {
                c = node->data;
 -              logger(LOG_DEBUG, " %s at %s options %x socket %d status %04x outbuf %d/%d/%d",
 -                         c->name, c->hostname, c->options, c->socket, bitfield_to_int(&c->status, sizeof c->status),
 -                         c->outbufsize, c->outbufstart, c->outbuflen);
 +              send_request(cdump, "%d %d %s at %s options %x socket %d status %04x",
 +                              CONTROL, REQ_DUMP_CONNECTIONS,
 +                              c->name, c->hostname, c->options, c->socket,
 +                              bitfield_to_int(&c->status, sizeof c->status));
        }
  
 -      logger(LOG_DEBUG, "End of connections.");
 +      return send_request(cdump, "%d %d", CONTROL, REQ_DUMP_CONNECTIONS);
  }
- bool read_connection_config(connection_t *c) {
-       char *fname;
-       bool x;
-       xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
-       x = read_config_file(c->config_tree, fname);
-       free(fname);
-       return x;
- }
diff --combined src/connection.h
index 0f2b1d6a44d86ac0b72f453245203e1a399d98f6,05e8b4bae165b79041934fac21015eb6657fb846..0fc49ef38d4fe19228d191d0db37b5707474d302
  #ifndef __TINC_CONNECTION_H__
  #define __TINC_CONNECTION_H__
  
 -#include <openssl/rsa.h>
 -#include <openssl/evp.h>
 -
 -#include "avl_tree.h"
 +#include "cipher.h"
 +#include "digest.h"
 +#include "rsa.h"
 +#include "splay_tree.h"
  
  #define OPTION_INDIRECT               0x0001
  #define OPTION_TCPONLY                0x0002
  #define OPTION_CLAMP_MSS      0x0008
  
  typedef struct connection_status_t {
 -      int pinged:1;                           /* sent ping */
 -      int active:1;                           /* 1 if active.. */
 -      int connecting:1;                       /* 1 if we are waiting for a non-blocking connect() to finish */
 -      int termreq:1;                          /* the termination of this connection was requested */
 -      int remove:1;                           /* Set to 1 if you want this connection removed */
 -      int timeout:1;                          /* 1 if gotten timeout */
 -      int encryptout:1;                       /* 1 if we can encrypt outgoing traffic */
 -      int decryptin:1;                        /* 1 if we have to decrypt incoming traffic */
 -      int mst:1;                              /* 1 if this connection is part of a minimum spanning tree */
 -      int unused:23;
 +              int pinged:1;                           /* sent ping */
 +              int active:1;                           /* 1 if active.. */
 +              int connecting:1;                       /* 1 if we are waiting for a non-blocking connect() to finish */
 +              int termreq:1;                          /* the termination of this connection was requested */
 +              int remove_unused:1;                            /* Set to 1 if you want this connection removed */
 +              int timeout_unused:1;                           /* 1 if gotten timeout */
 +              int encryptout:1;                       /* 1 if we can encrypt outgoing traffic */
 +              int decryptin:1;                        /* 1 if we have to decrypt incoming traffic */
 +              int mst:1;                              /* 1 if this connection is part of a minimum spanning tree */
 +              int control:1;
 +              int unused:22;
  } connection_status_t;
  
  #include "edge.h"
@@@ -67,30 -66,42 +67,30 @@@ typedef struct connection_t 
        struct node_t *node;            /* node associated with the other end */
        struct edge_t *edge;            /* edge associated with this connection */
  
 -      RSA *rsa_key;                           /* his public/private key */
 -      const EVP_CIPHER *incipher;     /* Cipher he will use to send data to us */
 -      const EVP_CIPHER *outcipher;    /* Cipher we will use to send data to him */
 -      EVP_CIPHER_CTX *inctx;          /* Context of encrypted meta data that will come from him to us */
 -      EVP_CIPHER_CTX *outctx;         /* Context of encrypted meta data that will be sent from us to him */
 -      char *inkey;                            /* His symmetric meta key + iv */
 -      char *outkey;                           /* Our symmetric meta key + iv */
 -      int inkeylength;                        /* Length of his key + iv */
 -      int outkeylength;                       /* Length of our key + iv */
 -      const EVP_MD *indigest;
 -      const EVP_MD *outdigest;
 +      rsa_t rsa;                      /* his public/private key */
 +      cipher_t incipher;              /* Cipher he will use to send data to us */
 +      cipher_t outcipher;             /* Cipher we will use to send data to him */
 +      digest_t indigest;
 +      digest_t outdigest;
 +
        int inmaclength;
        int outmaclength;
        int incompression;
        int outcompression;
 -      char *mychallenge;                      /* challenge we received from him */
 -      char *hischallenge;                     /* challenge we sent to him */
  
 -      char buffer[MAXBUFSIZE];        /* metadata input buffer */
 -      int buflen;                                     /* bytes read into buffer */
 -      int reqlen;                                     /* length of incoming request */
 +      char *hischallenge;             /* The challenge we sent to him */
 +
 +      struct bufferevent *buffer;                     /* buffer events on this metadata connection */
 +      struct event inevent;                           /* input event on this metadata connection */
        int tcplen;                                     /* length of incoming TCPpacket */
        int allow_request;                      /* defined if there's only one request possible */
  
 -      char *outbuf;                           /* metadata output buffer */
 -      int outbufstart;                        /* index of first meaningful byte in output buffer */
 -      int outbuflen;                          /* number of meaningful bytes in output buffer */
 -      int outbufsize;                         /* number of bytes allocated to output buffer */
 -
        time_t last_ping_time;          /* last time we saw some activity from the other end or pinged them */
 -      time_t last_flushed_time;       /* last time buffer was empty. Only meaningful if outbuflen > 0 */
  
 -      avl_tree_t *config_tree;        /* Pointer to configuration tree belonging to him */
 +      splay_tree_t *config_tree;      /* Pointer to configuration tree belonging to him */
  } connection_t;
  
 -extern avl_tree_t *connection_tree;
 +extern splay_tree_t *connection_tree;
  extern connection_t *broadcast;
  
  extern void init_connections(void);
@@@ -99,7 -110,6 +99,6 @@@ extern connection_t *new_connection(voi
  extern void free_connection(connection_t *);
  extern void connection_add(connection_t *);
  extern void connection_del(connection_t *);
 -extern void dump_connections(void);
 +extern bool dump_connections(struct connection_t *);
- extern bool read_connection_config(connection_t *);
  
  #endif                                                        /* __TINC_CONNECTION_H__ */
diff --combined src/cygwin/device.c
index 0058076465e72c1673d7c033bea9fb0c2330d91f,90966cd2419d0a7c724a00dba864ab074e37e47a..5c227fd72b70534db6d3b57bfdc95beb21822769
@@@ -38,8 -38,8 +38,8 @@@ char *device = NULL
  char *iface = NULL;
  static char *device_info = NULL;
  
- static int device_total_in = 0;
- static int device_total_out = 0;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
  
  static pid_t reader_pid;
  static int sp[2];
@@@ -68,18 -68,18 +68,18 @@@ bool setup_device(void) 
        }
  
        for (i = 0; ; i++) {
 -              len = sizeof(adapterid);
 +              len = sizeof adapterid;
                if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
                        break;
  
                /* Find out more about this adapter */
  
 -              snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
 +              snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
  
                  if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
                        continue;
  
 -              len = sizeof(adaptername);
 +              len = sizeof adaptername;
                err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
  
                RegCloseKey(key2);
                                continue;
                }
  
 -              snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
 +              snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
                device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
                if(device_handle != INVALID_HANDLE_VALUE) {
                        CloseHandle(device_handle);
        if(!iface)
                iface = xstrdup(adaptername);
  
 -      snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
 +      snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
        
        /* Now we are going to open this device twice: once for reading and once for writing.
           We do this because apparently it isn't possible to check for activity in the select() loop.
  
        /* Get MAC address from tap device */
  
 -      if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
 +      if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
                logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
                return false;
        }
                   It passes everything it reads to the socket. */
        
                char buf[MTU];
 -              long lenin;
 +              long inlen;
  
                CloseHandle(device_handle);
  
                /* Pass packets */
  
                for(;;) {
 -                      ReadFile(device_handle, buf, MTU, &lenin, NULL);
 -                      write(sp[1], buf, lenin);
 +                      ReadFile(device_handle, buf, MTU, &inlen, NULL);
 +                      write(sp[1], buf, inlen);
                }
        }
  
@@@ -225,15 -225,15 +225,15 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
 -      if((lenin = read(sp[0], packet->data, MTU)) <= 0) {
 +      if((inlen = read(sp[0], packet->data, MTU)) <= 0) {
                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
        }
        
 -      packet->len = lenin;
 +      packet->len = inlen;
  
        device_total_in += packet->len;
  
  }
  
  bool write_packet(vpn_packet_t *packet) {
 -      long lenout;
 +      long outlen;
  
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
  
 -      if(!WriteFile (device_handle, packet->data, packet->len, &lenout, NULL)) {
 +      if(!WriteFile (device_handle, packet->data, packet->len, &outlen, NULL)) {
                logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
                return false;
        }
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10d", device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10d", device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  }
diff --combined src/getopt.c
index b2f88b42305f174aa1ebe0668501e6a8da401ac3,0000000000000000000000000000000000000000..a6782ed667b46fccfb2a01b76ed18b5d4dcff591
mode 100644,000000..100644
--- /dev/null
@@@ -1,1042 -1,0 +1,1048 @@@
-                  if (argv[optind - 1][1] == '-')
-                   /* --option */
-                   fprintf (stderr,
-                    "%s: option `--%s' doesn't allow an argument\n",
-                    argv[0], pfound->name);
-                  else
-                   /* +option or -option */
-                   fprintf (stderr,
-                    "%s: option `%c%s' doesn't allow an argument\n",
-                    argv[0], argv[optind - 1][0], pfound->name);
 +/* Getopt for GNU.
 +   NOTE: getopt is now part of the C library, so if you don't know what
 +   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
 +   before changing it!
 +
 +   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
 +      Free Software Foundation, Inc.
 +
 +NOTE: The canonical source of this file is maintained with the GNU C Library.
 +Bugs can be reported to bug-glibc@prep.ai.mit.edu.
 +
 +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, 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.
 +*/
 +\f
 +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
 +   Ditto for AIX 3.2 and <stdlib.h>.  */
 +#ifndef _NO_PROTO
 +#define _NO_PROTO
 +#endif
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#if !defined (__STDC__) || !__STDC__
 +/* This is a separate conditional since some stdc systems
 +   reject `defined (const)'.  */
 +#ifndef const
 +#define const
 +#endif
 +#endif
 +
 +#include <stdio.h>
 +
++#ifdef HAVE_STRING_H
++#include <string.h>
++#endif
++
 +/* Comment out all this code if we are using the GNU C Library, and are not
 +   actually compiling the library itself.  This code is part of the GNU C
 +   Library, but also included in many other GNU distributions.  Compiling
 +   and linking in this code is a waste when using the GNU C library
 +   (especially if it is a shared library).  Rather than having every GNU
 +   program understand `configure --with-gnu-libc' and omit the object files,
 +   it is simpler to just do this in the source for each such file.  */
 +
 +#define GETOPT_INTERFACE_VERSION 2
 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
 +#include <gnu-versions.h>
 +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
 +#define ELIDE_CODE
 +#endif
 +#endif
 +
 +#ifndef ELIDE_CODE
 +
 +
 +/* This needs to come after some library #include
 +   to get __GNU_LIBRARY__ defined.  */
 +#ifdef        __GNU_LIBRARY__
 +/* Don't include stdlib.h for non-GNU C libraries because some of them
 +   contain conflicting prototypes for getopt.  */
 +#include <stdlib.h>
 +#include <unistd.h>
 +#endif        /* GNU C library.  */
 +
 +#ifdef VMS
 +#include <unixlib.h>
 +#if HAVE_STRING_H - 0
 +#include <string.h>
 +#endif
 +#endif
 +
 +#if defined (WIN32) && !defined (__CYGWIN32__)
 +/* It's not Unix, really.  See?  Capital letters.  */
 +#include <windows.h>
 +#define getpid() GetCurrentProcessId()
 +#endif
 +
 +/* This version of `getopt' appears to the caller like standard Unix `getopt'
 +   but it behaves differently for the user, since it allows the user
 +   to intersperse the options with the other arguments.
 +
 +   As `getopt' works, it permutes the elements of ARGV so that,
 +   when it is done, all the options precede everything else.  Thus
 +   all application programs are extended to handle flexible argument order.
 +
 +   Setting the environment variable POSIXLY_CORRECT disables permutation.
 +   Then the behavior is completely standard.
 +
 +   GNU application programs can use a third alternative mode in which
 +   they can distinguish the relative order of options and other arguments.  */
 +
 +#include "getopt.h"
 +
 +/* For communication from `getopt' to the caller.
 +   When `getopt' finds an option that takes an argument,
 +   the argument value is returned here.
 +   Also, when `ordering' is RETURN_IN_ORDER,
 +   each non-option ARGV-element is returned here.  */
 +
 +char *optarg = NULL;
 +
 +/* Index in ARGV of the next element to be scanned.
 +   This is used for communication to and from the caller
 +   and for communication between successive calls to `getopt'.
 +
 +   On entry to `getopt', zero means this is the first call; initialize.
 +
 +   When `getopt' returns -1, this is the index of the first of the
 +   non-option elements that the caller should itself scan.
 +
 +   Otherwise, `optind' communicates from one call to the next
 +   how much of ARGV has been scanned so far.  */
 +
 +/* 1003.2 says this must be 1 before any call.  */
 +int optind = 1;
 +
 +/* Formerly, initialization of getopt depended on optind==0, which
 +   causes problems with re-calling getopt as programs generally don't
 +   know that. */
 +
 +int __getopt_initialized = 0;
 +
 +/* The next char to be scanned in the option-element
 +   in which the last option character we returned was found.
 +   This allows us to pick up the scan where we left off.
 +
 +   If this is zero, or a null string, it means resume the scan
 +   by advancing to the next ARGV-element.  */
 +
 +static char *nextchar;
 +
 +/* Callers store zero here to inhibit the error message
 +   for unrecognized options.  */
 +
 +int opterr = 1;
 +
 +/* Set to an option character which was unrecognized.
 +   This must be initialized on some systems to avoid linking in the
 +   system's own getopt implementation.  */
 +
 +int optopt = '?';
 +
 +/* Describe how to deal with options that follow non-option ARGV-elements.
 +
 +   If the caller did not specify anything,
 +   the default is REQUIRE_ORDER if the environment variable
 +   POSIXLY_CORRECT is defined, PERMUTE otherwise.
 +
 +   REQUIRE_ORDER means don't recognize them as options;
 +   stop option processing when the first non-option is seen.
 +   This is what Unix does.
 +   This mode of operation is selected by either setting the environment
 +   variable POSIXLY_CORRECT, or using `+' as the first character
 +   of the list of option characters.
 +
 +   PERMUTE is the default.  We permute the contents of ARGV as we scan,
 +   so that eventually all the non-options are at the end.  This allows options
 +   to be given in any order, even with programs that were not written to
 +   expect this.
 +
 +   RETURN_IN_ORDER is an option available to programs that were written
 +   to expect options and other ARGV-elements in any order and that care about
 +   the ordering of the two.  We describe each non-option ARGV-element
 +   as if it were the argument of an option with character code 1.
 +   Using `-' as the first character of the list of option characters
 +   selects this mode of operation.
 +
 +   The special argument `--' forces an end of option-scanning regardless
 +   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
 +   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
 +
 +static enum
 +{
 +  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
 +} ordering;
 +
 +/* Value of POSIXLY_CORRECT environment variable.  */
 +static char *posixly_correct;
 +\f
 +#ifdef        __GNU_LIBRARY__
 +/* We want to avoid inclusion of string.h with non-GNU libraries
 +   because there are many ways it can cause trouble.
 +   On some systems, it contains special magic macros that don't work
 +   in GCC.  */
 +#include <string.h>
 +#define       my_index        strchr
 +#else
 +
 +/* Avoid depending on library functions or files
 +   whose names are inconsistent.  */
 +
 +char *getenv ();
 +
 +static char *
 +my_index (str, chr)
 +     const char *str;
 +     int chr;
 +{
 +  while (*str)
 +    {
 +      if (*str == chr)
 +      return (char *) str;
 +      str++;
 +    }
 +  return 0;
 +}
 +
 +/* If using GCC, we can safely declare strlen this way.
 +   If not using GCC, it is ok not to declare it.  */
 +#ifdef __GNUC__
 +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
 +   That was relevant to code that was here before.  */
 +#if !defined (__STDC__) || !__STDC__
 +/* gcc with -traditional declares the built-in strlen to return int,
 +   and has done so at least since version 2.4.5. -- rms.  */
 +extern int strlen (const char *);
 +#endif /* not __STDC__ */
 +#endif /* __GNUC__ */
 +
 +#endif /* not __GNU_LIBRARY__ */
 +\f
 +/* Handle permutation of arguments.  */
 +
 +/* Describe the part of ARGV that contains non-options that have
 +   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
 +   `last_nonopt' is the index after the last of them.  */
 +
 +static int first_nonopt;
 +static int last_nonopt;
 +
 +#ifdef _LIBC
 +/* Bash 2.0 gives us an environment variable containing flags
 +   indicating ARGV elements that should not be considered arguments.  */
 +
 +/* Defined in getopt_init.c  */
 +extern char *__getopt_nonoption_flags;
 +
 +static int nonoption_flags_max_len;
 +static int nonoption_flags_len;
 +
 +static int original_argc;
 +static char *const *original_argv;
 +
 +extern pid_t __libc_pid;
 +
 +/* Make sure the environment variable bash 2.0 puts in the environment
 +   is valid for the getopt call we must make sure that the ARGV passed
 +   to getopt is that one passed to the process.  */
 +static void
 +__attribute__ ((__unused__))
 +store_args_and_env (int argc, char *const *argv)
 +{
 +  /* XXX This is no good solution.  We should rather copy the args so
 +     that we can compare them later.  But we must not use malloc(3).  */
 +  original_argc = argc;
 +  original_argv = argv;
 +}
 +text_set_element (__libc_subinit, store_args_and_env);
 +
 +# define SWAP_FLAGS(ch1, ch2) \
 +  if (nonoption_flags_len > 0)                                                      \
 +    {                                                                       \
 +      char __tmp = __getopt_nonoption_flags[ch1];                           \
 +      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];        \
 +      __getopt_nonoption_flags[ch2] = __tmp;                                \
 +    }
 +#else /* !_LIBC */
 +# define SWAP_FLAGS(ch1, ch2)
 +#endif        /* _LIBC */
 +
 +/* Exchange two adjacent subsequences of ARGV.
 +   One subsequence is elements [first_nonopt,last_nonopt)
 +   which contains all the non-options that have been skipped so far.
 +   The other is elements [last_nonopt,optind), which contains all
 +   the options processed since those non-options were skipped.
 +
 +   `first_nonopt' and `last_nonopt' are relocated so that they describe
 +   the new indices of the non-options in ARGV after they are moved.  */
 +
 +#if defined (__STDC__) && __STDC__
 +static void exchange (char **);
 +#endif
 +
 +static void
 +exchange (argv)
 +     char **argv;
 +{
 +  int bottom = first_nonopt;
 +  int middle = last_nonopt;
 +  int top = optind;
 +  char *tem;
 +
 +  /* Exchange the shorter segment with the far end of the longer segment.
 +     That puts the shorter segment into the right place.
 +     It leaves the longer segment in the right place overall,
 +     but it consists of two parts that need to be swapped next.  */
 +
 +#ifdef _LIBC
 +  /* First make sure the handling of the `__getopt_nonoption_flags'
 +     string can work normally.  Our top argument must be in the range
 +     of the string.  */
 +  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
 +    {
 +      /* We must extend the array.  The user plays games with us and
 +       presents new arguments.  */
 +      char *new_str = malloc (top + 1);
 +      if (new_str == NULL)
 +      nonoption_flags_len = nonoption_flags_max_len = 0;
 +      else
 +      {
 +        memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
 +        memset (&new_str[nonoption_flags_max_len], '\0',
 +                top + 1 - nonoption_flags_max_len);
 +        nonoption_flags_max_len = top + 1;
 +        __getopt_nonoption_flags = new_str;
 +      }
 +    }
 +#endif
 +
 +  while (top > middle && middle > bottom)
 +    {
 +      if (top - middle > middle - bottom)
 +      {
 +        /* Bottom segment is the short one.  */
 +        int len = middle - bottom;
 +        register int i;
 +
 +        /* Swap it with the top part of the top segment.  */
 +        for (i = 0; i < len; i++)
 +          {
 +            tem = argv[bottom + i];
 +            argv[bottom + i] = argv[top - (middle - bottom) + i];
 +            argv[top - (middle - bottom) + i] = tem;
 +            SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
 +          }
 +        /* Exclude the moved bottom segment from further swapping.  */
 +        top -= len;
 +      }
 +      else
 +      {
 +        /* Top segment is the short one.  */
 +        int len = top - middle;
 +        register int i;
 +
 +        /* Swap it with the bottom part of the bottom segment.  */
 +        for (i = 0; i < len; i++)
 +          {
 +            tem = argv[bottom + i];
 +            argv[bottom + i] = argv[middle + i];
 +            argv[middle + i] = tem;
 +            SWAP_FLAGS (bottom + i, middle + i);
 +          }
 +        /* Exclude the moved top segment from further swapping.  */
 +        bottom += len;
 +      }
 +    }
 +
 +  /* Update records for the slots the non-options now occupy.  */
 +
 +  first_nonopt += (optind - last_nonopt);
 +  last_nonopt = optind;
 +}
 +
 +/* Initialize the internal data when the first call is made.  */
 +
 +#if defined (__STDC__) && __STDC__
 +static const char *_getopt_initialize (int, char *const *, const char *);
 +#endif
 +static const char *
 +_getopt_initialize (argc, argv, optstring)
 +     int argc;
 +     char *const *argv;
 +     const char *optstring;
 +{
 +  /* Start processing options with ARGV-element 1 (since ARGV-element 0
 +     is the program name); the sequence of previously skipped
 +     non-option ARGV-elements is empty.  */
 +
 +  first_nonopt = last_nonopt = optind;
 +
 +  nextchar = NULL;
 +
 +  posixly_correct = getenv ("POSIXLY_CORRECT");
 +
 +  /* Determine how to handle the ordering of options and nonoptions.  */
 +
 +  if (optstring[0] == '-')
 +    {
 +      ordering = RETURN_IN_ORDER;
 +      ++optstring;
 +    }
 +  else if (optstring[0] == '+')
 +    {
 +      ordering = REQUIRE_ORDER;
 +      ++optstring;
 +    }
 +  else if (posixly_correct != NULL)
 +    ordering = REQUIRE_ORDER;
 +  else
 +    ordering = PERMUTE;
 +
 +#ifdef _LIBC
 +  if (posixly_correct == NULL
 +      && argc == original_argc && argv == original_argv)
 +    {
 +      if (nonoption_flags_max_len == 0)
 +      {
 +        if (__getopt_nonoption_flags == NULL
 +            || __getopt_nonoption_flags[0] == '\0')
 +          nonoption_flags_max_len = -1;
 +        else
 +          {
 +            const char *orig_str = __getopt_nonoption_flags;
 +            int len = nonoption_flags_max_len = strlen (orig_str);
 +            if (nonoption_flags_max_len < argc)
 +              nonoption_flags_max_len = argc;
 +            __getopt_nonoption_flags =
 +              (char *) malloc (nonoption_flags_max_len);
 +            if (__getopt_nonoption_flags == NULL)
 +              nonoption_flags_max_len = -1;
 +            else
 +              {
 +                memcpy (__getopt_nonoption_flags, orig_str, len);
 +                memset (&__getopt_nonoption_flags[len], '\0',
 +                        nonoption_flags_max_len - len);
 +              }
 +          }
 +      }
 +      nonoption_flags_len = nonoption_flags_max_len;
 +    }
 +  else
 +    nonoption_flags_len = 0;
 +#endif
 +
 +  return optstring;
 +}
 +\f
 +/* Scan elements of ARGV (whose length is ARGC) for option characters
 +   given in OPTSTRING.
 +
 +   If an element of ARGV starts with '-', and is not exactly "-" or "--",
 +   then it is an option element.  The characters of this element
 +   (aside from the initial '-') are option characters.  If `getopt'
 +   is called repeatedly, it returns successively each of the option characters
 +   from each of the option elements.
 +
 +   If `getopt' finds another option character, it returns that character,
 +   updating `optind' and `nextchar' so that the next call to `getopt' can
 +   resume the scan with the following option character or ARGV-element.
 +
 +   If there are no more option characters, `getopt' returns -1.
 +   Then `optind' is the index in ARGV of the first ARGV-element
 +   that is not an option.  (The ARGV-elements have been permuted
 +   so that those that are not options now come last.)
 +
 +   OPTSTRING is a string containing the legitimate option characters.
 +   If an option character is seen that is not listed in OPTSTRING,
 +   return '?' after printing an error message.  If you set `opterr' to
 +   zero, the error message is suppressed but we still return '?'.
 +
 +   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
 +   so the following text in the same ARGV-element, or the text of the following
 +   ARGV-element, is returned in `optarg'.  Two colons mean an option that
 +   wants an optional arg; if there is text in the current ARGV-element,
 +   it is returned in `optarg', otherwise `optarg' is set to zero.
 +
 +   If OPTSTRING starts with `-' or `+', it requests different methods of
 +   handling the non-option ARGV-elements.
 +   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
 +
 +   Long-named options begin with `--' instead of `-'.
 +   Their names may be abbreviated as long as the abbreviation is unique
 +   or is an exact match for some defined option.  If they have an
 +   argument, it follows the option name in the same ARGV-element, separated
 +   from the option name by a `=', or else the in next ARGV-element.
 +   When `getopt' finds a long-named option, it returns 0 if that option's
 +   `flag' field is nonzero, the value of the option's `val' field
 +   if the `flag' field is zero.
 +
 +   The elements of ARGV aren't really const, because we permute them.
 +   But we pretend they're const in the prototype to be compatible
 +   with other systems.
 +
 +   LONGOPTS is a vector of `struct option' terminated by an
 +   element containing a name which is zero.
 +
 +   LONGIND returns the index in LONGOPT of the long-named option found.
 +   It is only valid when a long-named option has been found by the most
 +   recent call.
 +
 +   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
 +   long-named options.  */
 +
 +int
 +_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
 +     int argc;
 +     char *const *argv;
 +     const char *optstring;
 +     const struct option *longopts;
 +     int *longind;
 +     int long_only;
 +{
 +  optarg = NULL;
 +
 +  if (optind == 0 || !__getopt_initialized)
 +    {
 +      if (optind == 0)
 +      optind = 1;     /* Don't scan ARGV[0], the program name.  */
 +      optstring = _getopt_initialize (argc, argv, optstring);
 +      __getopt_initialized = 1;
 +    }
 +
 +  /* Test whether ARGV[optind] points to a non-option argument.
 +     Either it does not have option syntax, or there is an environment flag
 +     from the shell indicating it is not an option.  The later information
 +     is only used when the used in the GNU libc.  */
 +#ifdef _LIBC
 +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'              \
 +                   || (optind < nonoption_flags_len                         \
 +                       && __getopt_nonoption_flags[optind] == '1'))
 +#else
 +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
 +#endif
 +
 +  if (nextchar == NULL || *nextchar == '\0')
 +    {
 +      /* Advance to the next ARGV-element.  */
 +
 +      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
 +       moved back by the user (who may also have changed the arguments).  */
 +      if (last_nonopt > optind)
 +      last_nonopt = optind;
 +      if (first_nonopt > optind)
 +      first_nonopt = optind;
 +
 +      if (ordering == PERMUTE)
 +      {
 +        /* If we have just processed some options following some non-options,
 +           exchange them so that the options come first.  */
 +
 +        if (first_nonopt != last_nonopt && last_nonopt != optind)
 +          exchange ((char **) argv);
 +        else if (last_nonopt != optind)
 +          first_nonopt = optind;
 +
 +        /* Skip any additional non-options
 +           and extend the range of non-options previously skipped.  */
 +
 +        while (optind < argc && NONOPTION_P)
 +          optind++;
 +        last_nonopt = optind;
 +      }
 +
 +      /* The special ARGV-element `--' means premature end of options.
 +       Skip it like a null option,
 +       then exchange with previous non-options as if it were an option,
 +       then skip everything else like a non-option.  */
 +
 +      if (optind != argc && !strcmp (argv[optind], "--"))
 +      {
 +        optind++;
 +
 +        if (first_nonopt != last_nonopt && last_nonopt != optind)
 +          exchange ((char **) argv);
 +        else if (first_nonopt == last_nonopt)
 +          first_nonopt = optind;
 +        last_nonopt = argc;
 +
 +        optind = argc;
 +      }
 +
 +      /* If we have done all the ARGV-elements, stop the scan
 +       and back over any non-options that we skipped and permuted.  */
 +
 +      if (optind == argc)
 +      {
 +        /* Set the next-arg-index to point at the non-options
 +           that we previously skipped, so the caller will digest them.  */
 +        if (first_nonopt != last_nonopt)
 +          optind = first_nonopt;
 +        return -1;
 +      }
 +
 +      /* If we have come to a non-option and did not permute it,
 +       either stop the scan or describe it to the caller and pass it by.  */
 +
 +      if (NONOPTION_P)
 +      {
 +        if (ordering == REQUIRE_ORDER)
 +          return -1;
 +        optarg = argv[optind++];
 +        return 1;
 +      }
 +
 +      /* We have found another option-ARGV-element.
 +       Skip the initial punctuation.  */
 +
 +      nextchar = (argv[optind] + 1
 +                + (longopts != NULL && argv[optind][1] == '-'));
 +    }
 +
 +  /* Decode the current option-ARGV-element.  */
 +
 +  /* Check whether the ARGV-element is a long option.
 +
 +     If long_only and the ARGV-element has the form "-f", where f is
 +     a valid short option, don't consider it an abbreviated form of
 +     a long option that starts with f.  Otherwise there would be no
 +     way to give the -f short option.
 +
 +     On the other hand, if there's a long option "fubar" and
 +     the ARGV-element is "-fu", do consider that an abbreviation of
 +     the long option, just like "--fu", and not "-f" with arg "u".
 +
 +     This distinction seems to be the most useful approach.  */
 +
 +  if (longopts != NULL
 +      && (argv[optind][1] == '-'
 +        || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
 +    {
 +      char *nameend;
 +      const struct option *p;
 +      const struct option *pfound = NULL;
 +      int exact = 0;
 +      int ambig = 0;
 +      int indfound = -1;
 +      int option_index;
 +
 +      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
 +      /* Do nothing.  */ ;
 +
 +      /* Test all long options for either exact match
 +       or abbreviated matches.  */
 +      for (p = longopts, option_index = 0; p->name; p++, option_index++)
 +      if (!strncmp (p->name, nextchar, nameend - nextchar))
 +        {
 +          if ((unsigned int) (nameend - nextchar)
 +              == (unsigned int) strlen (p->name))
 +            {
 +              /* Exact match found.  */
 +              pfound = p;
 +              indfound = option_index;
 +              exact = 1;
 +              break;
 +            }
 +          else if (pfound == NULL)
 +            {
 +              /* First nonexact match found.  */
 +              pfound = p;
 +              indfound = option_index;
 +            }
 +          else
 +            /* Second or later nonexact match found.  */
 +            ambig = 1;
 +        }
 +
 +      if (ambig && !exact)
 +      {
 +        if (opterr)
 +          fprintf (stderr, "%s: option `%s' is ambiguous\n",
 +                   argv[0], argv[optind]);
 +        nextchar += strlen (nextchar);
 +        optind++;
 +        optopt = 0;
 +        return '?';
 +      }
 +
 +      if (pfound != NULL)
 +      {
 +        option_index = indfound;
 +        optind++;
 +        if (*nameend)
 +          {
 +            /* Don't test has_arg with >, because some C compilers don't
 +               allow it to be used on enums.  */
 +            if (pfound->has_arg)
 +              optarg = nameend + 1;
 +            else
 +              {
 +                if (opterr)
++                  {
++                   if (argv[optind - 1][1] == '-')
++                    /* --option */
++                    fprintf (stderr,
++                     "%s: option `--%s' doesn't allow an argument\n",
++                     argv[0], pfound->name);
++                   else
++                    /* +option or -option */
++                    fprintf (stderr,
++                     "%s: option `%c%s' doesn't allow an argument\n",
++                     argv[0], argv[optind - 1][0], pfound->name);
++                  }
 +
 +                nextchar += strlen (nextchar);
 +
 +                optopt = pfound->val;
 +                return '?';
 +              }
 +          }
 +        else if (pfound->has_arg == 1)
 +          {
 +            if (optind < argc)
 +              optarg = argv[optind++];
 +            else
 +              {
 +                if (opterr)
 +                  fprintf (stderr,
 +                         "%s: option `%s' requires an argument\n",
 +                         argv[0], argv[optind - 1]);
 +                nextchar += strlen (nextchar);
 +                optopt = pfound->val;
 +                return optstring[0] == ':' ? ':' : '?';
 +              }
 +          }
 +        nextchar += strlen (nextchar);
 +        if (longind != NULL)
 +          *longind = option_index;
 +        if (pfound->flag)
 +          {
 +            *(pfound->flag) = pfound->val;
 +            return 0;
 +          }
 +        return pfound->val;
 +      }
 +
 +      /* Can't find it as a long option.  If this is not getopt_long_only,
 +       or the option starts with '--' or is not a valid short
 +       option, then it's an error.
 +       Otherwise interpret it as a short option.  */
 +      if (!long_only || argv[optind][1] == '-'
 +        || my_index (optstring, *nextchar) == NULL)
 +      {
 +        if (opterr)
 +          {
 +            if (argv[optind][1] == '-')
 +              /* --option */
 +              fprintf (stderr, "%s: unrecognized option `--%s'\n",
 +                       argv[0], nextchar);
 +            else
 +              /* +option or -option */
 +              fprintf (stderr, "%s: unrecognized option `%c%s'\n",
 +                       argv[0], argv[optind][0], nextchar);
 +          }
 +        nextchar = (char *) "";
 +        optind++;
 +        optopt = 0;
 +        return '?';
 +      }
 +    }
 +
 +  /* Look at and handle the next short option-character.  */
 +
 +  {
 +    char c = *nextchar++;
 +    char *temp = my_index (optstring, c);
 +
 +    /* Increment `optind' when we start to process its last character.  */
 +    if (*nextchar == '\0')
 +      ++optind;
 +
 +    if (temp == NULL || c == ':')
 +      {
 +      if (opterr)
 +        {
 +          if (posixly_correct)
 +            /* 1003.2 specifies the format of this message.  */
 +            fprintf (stderr, "%s: illegal option -- %c\n",
 +                     argv[0], c);
 +          else
 +            fprintf (stderr, "%s: invalid option -- %c\n",
 +                     argv[0], c);
 +        }
 +      optopt = c;
 +      return '?';
 +      }
 +    /* Convenience. Treat POSIX -W foo same as long option --foo */
 +    if (temp[0] == 'W' && temp[1] == ';')
 +      {
 +      char *nameend;
 +      const struct option *p;
 +      const struct option *pfound = NULL;
 +      int exact = 0;
 +      int ambig = 0;
 +      int indfound = 0;
 +      int option_index;
 +
 +      /* This is an option that requires an argument.  */
 +      if (*nextchar != '\0')
 +        {
 +          optarg = nextchar;
 +          /* If we end this ARGV-element by taking the rest as an arg,
 +             we must advance to the next element now.  */
 +          optind++;
 +        }
 +      else if (optind == argc)
 +        {
 +          if (opterr)
 +            {
 +              /* 1003.2 specifies the format of this message.  */
 +              fprintf (stderr, "%s: option requires an argument -- %c\n",
 +                       argv[0], c);
 +            }
 +          optopt = c;
 +          if (optstring[0] == ':')
 +            c = ':';
 +          else
 +            c = '?';
 +          return c;
 +        }
 +      else
 +        /* We already incremented `optind' once;
 +           increment it again when taking next ARGV-elt as argument.  */
 +        optarg = argv[optind++];
 +
 +      /* optarg is now the argument, see if it's in the
 +         table of longopts.  */
 +
 +      for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
 +        /* Do nothing.  */ ;
 +
 +      /* Test all long options for either exact match
 +         or abbreviated matches.  */
 +      for (p = longopts, option_index = 0; p->name; p++, option_index++)
 +        if (!strncmp (p->name, nextchar, nameend - nextchar))
 +          {
 +            if ((unsigned int) (nameend - nextchar) == strlen (p->name))
 +              {
 +                /* Exact match found.  */
 +                pfound = p;
 +                indfound = option_index;
 +                exact = 1;
 +                break;
 +              }
 +            else if (pfound == NULL)
 +              {
 +                /* First nonexact match found.  */
 +                pfound = p;
 +                indfound = option_index;
 +              }
 +            else
 +              /* Second or later nonexact match found.  */
 +              ambig = 1;
 +          }
 +      if (ambig && !exact)
 +        {
 +          if (opterr)
 +            fprintf (stderr, "%s: option `-W %s' is ambiguous\n",
 +                     argv[0], argv[optind]);
 +          nextchar += strlen (nextchar);
 +          optind++;
 +          return '?';
 +        }
 +      if (pfound != NULL)
 +        {
 +          option_index = indfound;
 +          if (*nameend)
 +            {
 +              /* Don't test has_arg with >, because some C compilers don't
 +                 allow it to be used on enums.  */
 +              if (pfound->has_arg)
 +                optarg = nameend + 1;
 +              else
 +                {
 +                  if (opterr)
 +                    fprintf (stderr,
 +                             "%s: option `-W %s' doesn't allow an argument\n",
 +                             argv[0], pfound->name);
 +
 +                  nextchar += strlen (nextchar);
 +                  return '?';
 +                }
 +            }
 +          else if (pfound->has_arg == 1)
 +            {
 +              if (optind < argc)
 +                optarg = argv[optind++];
 +              else
 +                {
 +                  if (opterr)
 +                    fprintf (stderr,
 +                             "%s: option `%s' requires an argument\n",
 +                             argv[0], argv[optind - 1]);
 +                  nextchar += strlen (nextchar);
 +                  return optstring[0] == ':' ? ':' : '?';
 +                }
 +            }
 +          nextchar += strlen (nextchar);
 +          if (longind != NULL)
 +            *longind = option_index;
 +          if (pfound->flag)
 +            {
 +              *(pfound->flag) = pfound->val;
 +              return 0;
 +            }
 +          return pfound->val;
 +        }
 +        nextchar = NULL;
 +        return 'W';   /* Let the application handle it.   */
 +      }
 +    if (temp[1] == ':')
 +      {
 +      if (temp[2] == ':')
 +        {
 +          /* This is an option that accepts an argument optionally.  */
 +          if (*nextchar != '\0')
 +            {
 +              optarg = nextchar;
 +              optind++;
 +            }
 +          else
 +            optarg = NULL;
 +          nextchar = NULL;
 +        }
 +      else
 +        {
 +          /* This is an option that requires an argument.  */
 +          if (*nextchar != '\0')
 +            {
 +              optarg = nextchar;
 +              /* If we end this ARGV-element by taking the rest as an arg,
 +                 we must advance to the next element now.  */
 +              optind++;
 +            }
 +          else if (optind == argc)
 +            {
 +              if (opterr)
 +                {
 +                  /* 1003.2 specifies the format of this message.  */
 +                  fprintf (stderr,
 +                         "%s: option requires an argument -- %c\n",
 +                         argv[0], c);
 +                }
 +              optopt = c;
 +              if (optstring[0] == ':')
 +                c = ':';
 +              else
 +                c = '?';
 +            }
 +          else
 +            /* We already incremented `optind' once;
 +               increment it again when taking next ARGV-elt as argument.  */
 +            optarg = argv[optind++];
 +          nextchar = NULL;
 +        }
 +      }
 +    return c;
 +  }
 +}
 +
 +int
 +getopt (argc, argv, optstring)
 +     int argc;
 +     char *const *argv;
 +     const char *optstring;
 +{
 +  return _getopt_internal (argc, argv, optstring,
 +                         (const struct option *) 0,
 +                         (int *) 0,
 +                         0);
 +}
 +
 +#endif        /* Not ELIDE_CODE.  */
 +\f
 +#ifdef TEST
 +
 +/* Compile with -DTEST to make an executable for use in testing
 +   the above definition of `getopt'.  */
 +
 +int
 +main (argc, argv)
 +     int argc;
 +     char **argv;
 +{
 +  int c;
 +  int digit_optind = 0;
 +
 +  while (1)
 +    {
 +      int this_option_optind = optind ? optind : 1;
 +
 +      c = getopt (argc, argv, "abc:d:0123456789");
 +      if (c == -1)
 +      break;
 +
 +      switch (c)
 +      {
 +      case '0':
 +      case '1':
 +      case '2':
 +      case '3':
 +      case '4':
 +      case '5':
 +      case '6':
 +      case '7':
 +      case '8':
 +      case '9':
 +        if (digit_optind != 0 && digit_optind != this_option_optind)
 +          printf ("digits occur in two different argv-elements.\n");
 +        digit_optind = this_option_optind;
 +        printf ("option %c\n", c);
 +        break;
 +
 +      case 'a':
 +        printf ("option a\n");
 +        break;
 +
 +      case 'b':
 +        printf ("option b\n");
 +        break;
 +
 +      case 'c':
 +        printf ("option c with value `%s'\n", optarg);
 +        break;
 +
 +      case '?':
 +        break;
 +
 +      default:
 +        printf ("?? getopt returned character code 0%o ??\n", c);
 +      }
 +    }
 +
 +  if (optind < argc)
 +    {
 +      printf ("non-option ARGV-elements: ");
 +      while (optind < argc)
 +      printf ("%s ", argv[optind++]);
 +      printf ("\n");
 +    }
 +
 +  exit (0);
 +}
 +
 +#endif /* TEST */
diff --combined src/linux/device.c
index 72becd7715c2e82e6d426df57b23c21887d05c48,6c828c01d84f1909ab15ae8093a7ab56c7f71e9e..4dbe38d5d2858e876d32da2b2160e7a66a77a95e
@@@ -47,8 -47,8 +47,8 @@@ char *iface = NULL
  static char ifrname[IFNAMSIZ];
  static char *device_info;
  
- static int device_total_in = 0;
- static int device_total_out = 0;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
  
  bool setup_device(void) {
        struct ifreq ifr;
@@@ -61,7 -61,7 +61,7 @@@
                if (netname != NULL)
                        iface = xstrdup(netname);
  #else
-               iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device);
+               iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
  #endif
        device_fd = open(device, O_RDWR | O_NONBLOCK);
  
@@@ -73,7 -73,7 +73,7 @@@
  #ifdef HAVE_LINUX_IF_TUN_H
        /* Ok now check if this is an old ethertap or a new tun/tap thingie */
  
 -      memset(&ifr, 0, sizeof(ifr));
 +      memset(&ifr, 0, sizeof ifr);
        if(routing_mode == RMODE_ROUTER) {
                ifr.ifr_flags = IFF_TUN;
                device_type = DEVICE_TYPE_TUN;
                device_type = DEVICE_TYPE_ETHERTAP;
                if(iface)
                        free(iface);
-               iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device);
+               iface = xstrdup(strrchr(device, '/') ? strrchr(device, '/') + 1 : device);
        }
  
        logger(LOG_INFO, "%s is a %s", device, device_info);
@@@ -121,41 -121,41 +121,41 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
        
        switch(device_type) {
                case DEVICE_TYPE_TUN:
 -                      lenin = read(device_fd, packet->data + 10, MTU - 10);
 +                      inlen = read(device_fd, packet->data + 10, MTU - 10);
  
 -                      if(lenin <= 0) {
 +                      if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s",
                                           device_info, device, strerror(errno));
                                return false;
                        }
  
 -                      packet->len = lenin + 10;
 +                      packet->len = inlen + 10;
                        break;
                case DEVICE_TYPE_TAP:
 -                      lenin = read(device_fd, packet->data, MTU);
 +                      inlen = read(device_fd, packet->data, MTU);
  
 -                      if(lenin <= 0) {
 +                      if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s",
                                           device_info, device, strerror(errno));
                                return false;
                        }
  
 -                      packet->len = lenin;
 +                      packet->len = inlen;
                        break;
                case DEVICE_TYPE_ETHERTAP:
 -                      lenin = read(device_fd, packet->data - 2, MTU + 2);
 +                      inlen = read(device_fd, packet->data - 2, MTU + 2);
  
 -                      if(lenin <= 0) {
 +                      if(inlen <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s",
                                           device_info, device, strerror(errno));
                                return false;
                        }
  
 -                      packet->len = lenin - 2;
 +                      packet->len = inlen - 2;
                        break;
        }
  
@@@ -205,6 -205,6 +205,6 @@@ bool write_packet(vpn_packet_t *packet
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10d", device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10d", device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  }
diff --combined src/logger.c
index 35a6a38ae1d5bced37f15151c1d928dec7abb05e,bc20438cc83cc3f07c13ad9115b5fc1699e8a9bb..4c0d2312939c43330e7e29a07a94e845fa5e5291
@@@ -85,8 -85,8 +85,8 @@@ void logger(int priority, const char *f
  #ifdef HAVE_MINGW
                        {
                                char message[4096];
-                               char *messages[] = {message};
+                               const char *messages[] = {message};
 -                              vsnprintf(message, sizeof(message), format, ap);
 +                              vsnprintf(message, sizeof message, format, ap);
                                ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
                        }
  #else
@@@ -96,7 -96,7 +96,7 @@@
  #else
                        {
                                char message[4096];
 -                              vsnprintf(message, sizeof(message), format, ap);
 +                              vsnprintf(message, sizeof message, format, ap);
                                syslog(priority, "%s", message);
                        }
  #endif
diff --combined src/memcmp.c
index 9d8083385f4d4bf15e480fa2d8ab5f1558ca317f,0000000000000000000000000000000000000000..8103e1a7ff53f097faca1ec5e94089a676a83e9e
mode 100644,000000..100644
--- /dev/null
@@@ -1,391 -1,0 +1,391 @@@
-   long int srcp1 = (long int) &a;
-   long int srcp2 = (long int) &b;
 +/* Copyright (C) 1991, 1993, 1995, 1997, 1998 Free Software Foundation, Inc.
 +   Contributed by Torbjorn Granlund (tege@sics.se).
 +
 +   NOTE: The canonical source of this file is maintained with the GNU C Library.
 +   Bugs can be reported to bug-glibc@prep.ai.mit.edu.
 +
 +   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, 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.
 +*/
 +
 +#ifdef HAVE_CONFIG_H
 +# include "config.h"
 +#endif
 +
 +#undef        __ptr_t
 +#if defined __cplusplus || (defined __STDC__ && __STDC__)
 +# define __ptr_t      void *
 +#else /* Not C++ or ANSI C.  */
 +# undef       const
 +# define const
 +# define __ptr_t      char *
 +#endif /* C++ or ANSI C.  */
 +
 +#ifndef __P
 +# if defined __GNUC__ || (defined __STDC__ && __STDC__)
 +#  define __P(args) args
 +# else
 +#  define __P(args) ()
 +# endif  /* GCC.  */
 +#endif  /* Not __P.  */
 +
 +#if defined HAVE_STRING_H || defined _LIBC
 +# include <string.h>
 +#endif
 +
 +#undef memcmp
 +
 +#ifdef _LIBC
 +
 +# include <memcopy.h>
 +
 +#else /* Not in the GNU C library.  */
 +
 +# include <sys/types.h>
 +
 +/* Type to use for aligned memory operations.
 +   This should normally be the biggest type supported by a single load
 +   and store.  Must be an unsigned type.  */
 +# define op_t unsigned long int
 +# define OPSIZ        (sizeof(op_t))
 +
 +/* Threshold value for when to enter the unrolled loops.  */
 +# define OP_T_THRES   16
 +
 +/* Type to use for unaligned operations.  */
 +typedef unsigned char byte;
 +
 +# ifndef WORDS_BIGENDIAN
 +#  define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
 +# else
 +#  define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2)))
 +# endif
 +
 +#endif        /* In the GNU C library.  */
 +
 +#ifdef WORDS_BIGENDIAN
 +# define CMP_LT_OR_GT(a, b) ((a) > (b) ? 1 : -1)
 +#else
 +# define CMP_LT_OR_GT(a, b) memcmp_bytes ((a), (b))
 +#endif
 +
 +/* BE VERY CAREFUL IF YOU CHANGE THIS CODE!  */
 +
 +/* The strategy of this memcmp is:
 +
 +   1. Compare bytes until one of the block pointers is aligned.
 +
 +   2. Compare using memcmp_common_alignment or
 +      memcmp_not_common_alignment, regarding the alignment of the other
 +      block after the initial byte operations.  The maximum number of
 +      full words (of type op_t) are compared in this way.
 +
 +   3. Compare the few remaining bytes.  */
 +
 +#ifndef WORDS_BIGENDIAN
 +/* memcmp_bytes -- Compare A and B bytewise in the byte order of the machine.
 +   A and B are known to be different.
 +   This is needed only on little-endian machines.  */
 +
 +static int memcmp_bytes __P((op_t, op_t));
 +
 +# ifdef  __GNUC__
 +__inline
 +# endif
 +static int
 +memcmp_bytes (a, b)
 +     op_t a, b;
 +{
- static int memcmp_common_alignment __P((long, long, size_t));
++  intptr_t srcp1 = (intptr_t) &a;
++  intptr_t srcp2 = (intptr_t) &b;
 +  op_t a0, b0;
 +
 +  do
 +    {
 +      a0 = ((byte *) srcp1)[0];
 +      b0 = ((byte *) srcp2)[0];
 +      srcp1 += 1;
 +      srcp2 += 1;
 +    }
 +  while (a0 == b0);
 +  return a0 - b0;
 +}
 +#endif
 +
-      long int srcp1;
-      long int srcp2;
++static int memcmp_common_alignment __P((intptr_t, intptr_t, size_t));
 +
 +/* memcmp_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN `op_t'
 +   objects (not LEN bytes!).  Both SRCP1 and SRCP2 should be aligned for
 +   memory operations on `op_t's.  */
 +#ifdef        __GNUC__
 +__inline
 +#endif
 +static int
 +memcmp_common_alignment (srcp1, srcp2, len)
- static int memcmp_not_common_alignment __P((long, long, size_t));
++     intptr_t srcp1;
++     intptr_t srcp2;
 +     size_t len;
 +{
 +  op_t a0, a1;
 +  op_t b0, b1;
 +
 +  switch (len % 4)
 +    {
 +    default: /* Avoid warning about uninitialized local variables.  */
 +    case 2:
 +      a0 = ((op_t *) srcp1)[0];
 +      b0 = ((op_t *) srcp2)[0];
 +      srcp1 -= 2 * OPSIZ;
 +      srcp2 -= 2 * OPSIZ;
 +      len += 2;
 +      goto do1;
 +    case 3:
 +      a1 = ((op_t *) srcp1)[0];
 +      b1 = ((op_t *) srcp2)[0];
 +      srcp1 -= OPSIZ;
 +      srcp2 -= OPSIZ;
 +      len += 1;
 +      goto do2;
 +    case 0:
 +      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
 +      return 0;
 +      a0 = ((op_t *) srcp1)[0];
 +      b0 = ((op_t *) srcp2)[0];
 +      goto do3;
 +    case 1:
 +      a1 = ((op_t *) srcp1)[0];
 +      b1 = ((op_t *) srcp2)[0];
 +      srcp1 += OPSIZ;
 +      srcp2 += OPSIZ;
 +      len -= 1;
 +      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
 +      goto do0;
 +      /* Fall through.  */
 +    }
 +
 +  do
 +    {
 +      a0 = ((op_t *) srcp1)[0];
 +      b0 = ((op_t *) srcp2)[0];
 +      if (a1 != b1)
 +      return CMP_LT_OR_GT (a1, b1);
 +
 +    do3:
 +      a1 = ((op_t *) srcp1)[1];
 +      b1 = ((op_t *) srcp2)[1];
 +      if (a0 != b0)
 +      return CMP_LT_OR_GT (a0, b0);
 +
 +    do2:
 +      a0 = ((op_t *) srcp1)[2];
 +      b0 = ((op_t *) srcp2)[2];
 +      if (a1 != b1)
 +      return CMP_LT_OR_GT (a1, b1);
 +
 +    do1:
 +      a1 = ((op_t *) srcp1)[3];
 +      b1 = ((op_t *) srcp2)[3];
 +      if (a0 != b0)
 +      return CMP_LT_OR_GT (a0, b0);
 +
 +      srcp1 += 4 * OPSIZ;
 +      srcp2 += 4 * OPSIZ;
 +      len -= 4;
 +    }
 +  while (len != 0);
 +
 +  /* This is the right position for do0.  Please don't move
 +     it into the loop.  */
 + do0:
 +  if (a1 != b1)
 +    return CMP_LT_OR_GT (a1, b1);
 +  return 0;
 +}
 +
-      long int srcp1;
-      long int srcp2;
++static int memcmp_not_common_alignment __P((intptr_t, intptr_t, size_t));
 +
 +/* memcmp_not_common_alignment -- Compare blocks at SRCP1 and SRCP2 with LEN
 +   `op_t' objects (not LEN bytes!).  SRCP2 should be aligned for memory
 +   operations on `op_t', but SRCP1 *should be unaligned*.  */
 +#ifdef        __GNUC__
 +__inline
 +#endif
 +static int
 +memcmp_not_common_alignment (srcp1, srcp2, len)
-   long int srcp1 = (long int) s1;
-   long int srcp2 = (long int) s2;
++     intptr_t srcp1;
++     intptr_t srcp2;
 +     size_t len;
 +{
 +  op_t a0, a1, a2, a3;
 +  op_t b0, b1, b2, b3;
 +  op_t x;
 +  int shl, shr;
 +
 +  /* Calculate how to shift a word read at the memory operation
 +     aligned srcp1 to make it aligned for comparison.  */
 +
 +  shl = 8 * (srcp1 % OPSIZ);
 +  shr = 8 * OPSIZ - shl;
 +
 +  /* Make SRCP1 aligned by rounding it down to the beginning of the `op_t'
 +     it points in the middle of.  */
 +  srcp1 &= -OPSIZ;
 +
 +  switch (len % 4)
 +    {
 +    default: /* Avoid warning about uninitialized local variables.  */
 +    case 2:
 +      a1 = ((op_t *) srcp1)[0];
 +      a2 = ((op_t *) srcp1)[1];
 +      b2 = ((op_t *) srcp2)[0];
 +      srcp1 -= 1 * OPSIZ;
 +      srcp2 -= 2 * OPSIZ;
 +      len += 2;
 +      goto do1;
 +    case 3:
 +      a0 = ((op_t *) srcp1)[0];
 +      a1 = ((op_t *) srcp1)[1];
 +      b1 = ((op_t *) srcp2)[0];
 +      srcp2 -= 1 * OPSIZ;
 +      len += 1;
 +      goto do2;
 +    case 0:
 +      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
 +      return 0;
 +      a3 = ((op_t *) srcp1)[0];
 +      a0 = ((op_t *) srcp1)[1];
 +      b0 = ((op_t *) srcp2)[0];
 +      srcp1 += 1 * OPSIZ;
 +      goto do3;
 +    case 1:
 +      a2 = ((op_t *) srcp1)[0];
 +      a3 = ((op_t *) srcp1)[1];
 +      b3 = ((op_t *) srcp2)[0];
 +      srcp1 += 2 * OPSIZ;
 +      srcp2 += 1 * OPSIZ;
 +      len -= 1;
 +      if (OP_T_THRES <= 3 * OPSIZ && len == 0)
 +      goto do0;
 +      /* Fall through.  */
 +    }
 +
 +  do
 +    {
 +      a0 = ((op_t *) srcp1)[0];
 +      b0 = ((op_t *) srcp2)[0];
 +      x = MERGE(a2, shl, a3, shr);
 +      if (x != b3)
 +      return CMP_LT_OR_GT (x, b3);
 +
 +    do3:
 +      a1 = ((op_t *) srcp1)[1];
 +      b1 = ((op_t *) srcp2)[1];
 +      x = MERGE(a3, shl, a0, shr);
 +      if (x != b0)
 +      return CMP_LT_OR_GT (x, b0);
 +
 +    do2:
 +      a2 = ((op_t *) srcp1)[2];
 +      b2 = ((op_t *) srcp2)[2];
 +      x = MERGE(a0, shl, a1, shr);
 +      if (x != b1)
 +      return CMP_LT_OR_GT (x, b1);
 +
 +    do1:
 +      a3 = ((op_t *) srcp1)[3];
 +      b3 = ((op_t *) srcp2)[3];
 +      x = MERGE(a1, shl, a2, shr);
 +      if (x != b2)
 +      return CMP_LT_OR_GT (x, b2);
 +
 +      srcp1 += 4 * OPSIZ;
 +      srcp2 += 4 * OPSIZ;
 +      len -= 4;
 +    }
 +  while (len != 0);
 +
 +  /* This is the right position for do0.  Please don't move
 +     it into the loop.  */
 + do0:
 +  x = MERGE(a2, shl, a3, shr);
 +  if (x != b3)
 +    return CMP_LT_OR_GT (x, b3);
 +  return 0;
 +}
 +
 +int
 +rpl_memcmp (s1, s2, len)
 +     const __ptr_t s1;
 +     const __ptr_t s2;
 +     size_t len;
 +{
 +  op_t a0;
 +  op_t b0;
++  intptr_t srcp1 = (intptr_t) s1;
++  intptr_t srcp2 = (intptr_t) s2;
 +  op_t res;
 +
 +  if (len >= OP_T_THRES)
 +    {
 +      /* There are at least some bytes to compare.  No need to test
 +       for LEN == 0 in this alignment loop.  */
 +      while (srcp2 % OPSIZ != 0)
 +      {
 +        a0 = ((byte *) srcp1)[0];
 +        b0 = ((byte *) srcp2)[0];
 +        srcp1 += 1;
 +        srcp2 += 1;
 +        res = a0 - b0;
 +        if (res != 0)
 +          return res;
 +        len -= 1;
 +      }
 +
 +      /* SRCP2 is now aligned for memory operations on `op_t'.
 +       SRCP1 alignment determines if we can do a simple,
 +       aligned compare or need to shuffle bits.  */
 +
 +      if (srcp1 % OPSIZ == 0)
 +      res = memcmp_common_alignment (srcp1, srcp2, len / OPSIZ);
 +      else
 +      res = memcmp_not_common_alignment (srcp1, srcp2, len / OPSIZ);
 +      if (res != 0)
 +      return res;
 +
 +      /* Number of bytes remaining in the interval [0..OPSIZ-1].  */
 +      srcp1 += len & -OPSIZ;
 +      srcp2 += len & -OPSIZ;
 +      len %= OPSIZ;
 +    }
 +
 +  /* There are just a few bytes to compare.  Use byte memory operations.  */
 +  while (len != 0)
 +    {
 +      a0 = ((byte *) srcp1)[0];
 +      b0 = ((byte *) srcp2)[0];
 +      srcp1 += 1;
 +      srcp2 += 1;
 +      res = a0 - b0;
 +      if (res != 0)
 +      return res;
 +      len -= 1;
 +    }
 +
 +  return 0;
 +}
 +
 +#ifdef weak_alias
 +# undef bcmp
 +weak_alias (memcmp, bcmp)
 +#endif
diff --combined src/mingw/device.c
index b67d0d349033f70177640f561f9c73c5fb8cd60b,cbaa19e51d2296a3243ee1b1bebbb92ac467e52f..c6eb908b92f205333ddb39f11e07cad456837050
@@@ -38,8 -38,8 +38,8 @@@ char *device = NULL
  char *iface = NULL;
  static char *device_info = NULL;
  
- static int device_total_in = 0;
- static int device_total_out = 0;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
  
  extern char *myport;
  
@@@ -117,18 -117,18 +117,18 @@@ bool setup_device(void) 
        }
  
        for (i = 0; ; i++) {
 -              len = sizeof(adapterid);
 +              len = sizeof adapterid;
                if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL))
                        break;
  
                /* Find out more about this adapter */
  
 -              snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
 +              snprintf(regpath, sizeof regpath, "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
  
                  if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2))
                        continue;
  
 -              len = sizeof(adaptername);
 +              len = sizeof adaptername;
                err = RegQueryValueEx(key2, "Name", 0, 0, adaptername, &len);
  
                RegCloseKey(key2);
                                continue;
                }
  
 -              snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
 +              snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
                device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
                if(device_handle != INVALID_HANDLE_VALUE) {
                        found = true;
        /* Try to open the corresponding tap device */
  
        if(device_handle == INVALID_HANDLE_VALUE) {
 -              snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
 +              snprintf(tapname, sizeof tapname, USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
                device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
        }
        
  
        /* Get MAC address from tap device */
  
 -      if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
 +      if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof mymac.x, mymac.x, sizeof mymac.x, &len, 0)) {
                logger(LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
                return false;
        }
        /* Set media status for newer TAP-Win32 devices */
  
        status = true;
 -      DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
 +      DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof status, &status, sizeof status, &len, NULL);
  
        device_info = "Windows tap device";
  
@@@ -229,13 -229,13 +229,13 @@@ bool read_packet(vpn_packet_t *packet) 
  }
  
  bool write_packet(vpn_packet_t *packet) {
 -      long lenout;
 +      long outlen;
        OVERLAPPED overlapped = {0};
  
        ifdebug(TRAFFIC) logger(LOG_DEBUG, "Writing packet of %d bytes to %s",
                           packet->len, device_info);
  
 -      if(!WriteFile(device_handle, packet->data, packet->len, &lenout, &overlapped)) {
 +      if(!WriteFile(device_handle, packet->data, packet->len, &outlen, &overlapped)) {
                logger(LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
                return false;
        }
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10d", device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10d", device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  }
diff --combined src/net.c
index 0e5823641de349b1bb2321623be9820e0a37891c,08e3cad3ee1bf03a950748209703c47cdc943b7e..7d44d17c378f0bbbf2821bd6b4432dd43f5d6705
+++ b/src/net.c
  
  #include "system.h"
  
 -#include <openssl/rand.h>
 -
  #include "utils.h"
 -#include "avl_tree.h"
 +#include "splay_tree.h"
  #include "conf.h"
  #include "connection.h"
  #include "device.h"
 -#include "event.h"
  #include "graph.h"
  #include "logger.h"
  #include "meta.h"
  #include "netutl.h"
  #include "process.h"
  #include "protocol.h"
 -#include "route.h"
  #include "subnet.h"
  #include "xalloc.h"
  
 -bool do_purge = false;
 -volatile bool running = false;
 -
 -time_t now = 0;
+ int contradicting_add_edge = 0;
+ int contradicting_del_edge = 0;
  /* Purge edges and subnets of unreachable nodes. Use carefully. */
  
 -static void purge(void) {
 -      avl_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
 +void purge(void) {
 +      splay_node_t *nnode, *nnext, *enode, *enext, *snode, *snext;
        node_t *n;
        edge_t *e;
        subnet_t *s;
        }
  }
  
 -/*
 -  put all file descriptors in an fd_set array
 -  While we're at it, purge stuff that needs to be removed.
 -*/
 -static int build_fdset(fd_set *readset, fd_set *writeset) {
 -      avl_node_t *node, *next;
 -      connection_t *c;
 -      int i, max = 0;
 -
 -      FD_ZERO(readset);
 -      FD_ZERO(writeset);
 -
 -      for(node = connection_tree->head; node; node = next) {
 -              next = node->next;
 -              c = node->data;
 -
 -              if(c->status.remove) {
 -                      connection_del(c);
 -                      if(!connection_tree->head)
 -                              purge();
 -              } else {
 -                      FD_SET(c->socket, readset);
 -                      if(c->outbuflen > 0)
 -                              FD_SET(c->socket, writeset);
 -                      if(c->socket > max)
 -                              max = c->socket;
 -              }
 -      }
 -
 -      for(i = 0; i < listen_sockets; i++) {
 -              FD_SET(listen_socket[i].tcp, readset);
 -              if(listen_socket[i].tcp > max)
 -                      max = listen_socket[i].tcp;
 -              FD_SET(listen_socket[i].udp, readset);
 -              if(listen_socket[i].udp > max)
 -                      max = listen_socket[i].udp;
 -      }
 -
 -      if(device_fd >= 0)
 -              FD_SET(device_fd, readset);
 -      if(device_fd > max)
 -              max = device_fd;
 -      
 -      return max;
 -}
 -
  /*
    Terminate a connection:
    - Close the socket
    - Deactivate the host
  */
  void terminate_connection(connection_t *c, bool report) {
 -      if(c->status.remove)
 -              return;
 -
        ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Closing connection with %s (%s)",
                           c->name, c->hostname);
  
 -      c->status.remove = true;
        c->status.active = false;
  
        if(c->node)
  
        /* Check if this was our outgoing connection */
  
 -      if(c->outgoing) {
 +      if(c->outgoing)
                retry_outgoing(c->outgoing);
 -              c->outgoing = NULL;
 -      }
  
 -      free(c->outbuf);
 -      c->outbuf = NULL;
 -      c->outbuflen = 0;
 -      c->outbufsize = 0;
 -      c->outbufstart = 0;
 +      connection_del(c);
  }
  
  /*
    end does not reply in time, we consider them dead
    and close the connection.
  */
 -static void check_dead_connections(void) {
 -      avl_node_t *node, *next;
 +static void timeout_handler(int fd, short events, void *event) {
 +      splay_node_t *node, *next;
        connection_t *c;
 +      time_t now = time(NULL);
  
        for(node = connection_tree->head; node; node = next) {
                next = node->next;
                                if(c->status.pinged) {
                                        ifdebug(CONNECTIONS) logger(LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds",
                                                           c->name, c->hostname, now - c->last_ping_time);
 -                                      c->status.timeout = true;
                                        terminate_connection(c, true);
 +                                      continue;
                                } else if(c->last_ping_time + pinginterval < now) {
                                        send_ping(c);
                                }
                        } else {
 -                              if(c->status.remove) {
 -                                      logger(LOG_WARNING, "Old connection_t for %s (%s) status %04x still lingering, deleting...",
 -                                                 c->name, c->hostname, bitfield_to_int(&c->status, sizeof c->status));
 -                                      connection_del(c);
 -                                      continue;
 -                              }
 -                              ifdebug(CONNECTIONS) logger(LOG_WARNING, "Timeout from %s (%s) during authentication",
 -                                                 c->name, c->hostname);
                                if(c->status.connecting) {
 +                                      ifdebug(CONNECTIONS)
 +                                              logger(LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname);
                                        c->status.connecting = false;
                                        closesocket(c->socket);
                                        do_outgoing_connection(c);
                                } else {
 +                                      ifdebug(CONNECTIONS) logger(LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname);
                                        terminate_connection(c, false);
 +                                      continue;
                                }
                        }
                }
 -
 -              if(c->outbuflen > 0 && c->last_flushed_time + pingtimeout < now) {
 -                      if(c->status.active) {
 -                              ifdebug(CONNECTIONS) logger(LOG_INFO,
 -                                              "%s (%s) could not flush for %ld seconds (%d bytes remaining)",
 -                                              c->name, c->hostname, now - c->last_flushed_time, c->outbuflen);
 -                              c->status.timeout = true;
 -                              terminate_connection(c, true);
 -                      }
 -              }
 -      }
 -}
 -
 -/*
 -  check all connections to see if anything
 -  happened on their sockets
 -*/
 -static void check_network_activity(fd_set * readset, fd_set * writeset) {
 -      connection_t *c;
 -      avl_node_t *node;
 -      int result, i;
 -      socklen_t len = sizeof(result);
 -      vpn_packet_t packet;
 -      static int errors = 0;
 -
 -      /* check input from kernel */
 -      if(device_fd >= 0 && FD_ISSET(device_fd, readset)) {
 -              if(read_packet(&packet)) {
 -                      errors = 0;
 -                      packet.priority = 0;
 -                      route(myself, &packet);
 -              } else {
 -                      usleep(errors * 50000);
 -                      errors++;
 -                      if(errors > 10) {
 -                              logger(LOG_ERR, "Too many errors from %s, exiting!", device);
 -                              running = false;
 -                      }
 -              }
        }
  
 -      /* check meta connections */
 -      for(node = connection_tree->head; node; node = node->next) {
 -              c = node->data;
++      if(contradicting_del_edge && contradicting_add_edge) {
++              logger(LOG_WARNING, "Possible node with same Name as us!");
 -              if(c->status.remove)
 -                      continue;
 -
 -              if(FD_ISSET(c->socket, readset)) {
 -                      if(c->status.connecting) {
 -                              c->status.connecting = false;
 -                              getsockopt(c->socket, SOL_SOCKET, SO_ERROR, (void *)&result, &len);
++              if(rand() % 3 == 0) {
++                      logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!");
++                      event_loopexit(NULL);
++                      return;
++              }
 -                              if(!result)
 -                                      finish_connecting(c);
 -                              else {
 -                                      ifdebug(CONNECTIONS) logger(LOG_DEBUG,
 -                                                         "Error while connecting to %s (%s): %s",
 -                                                         c->name, c->hostname, sockstrerror(result));
 -                                      closesocket(c->socket);
 -                                      do_outgoing_connection(c);
 -                                      continue;
 -                              }
 -                      }
++              contradicting_add_edge = 0;
++              contradicting_del_edge = 0;
++      }
 -                      if(!receive_meta(c)) {
 -                              terminate_connection(c, c->status.active);
 -                              continue;
 -                      }
 -              }
 +      event_add(event, &(struct timeval){pingtimeout, 0});
 +}
  
 -              if(FD_ISSET(c->socket, writeset)) {
 -                      if(!flush_meta(c)) {
 -                              terminate_connection(c, c->status.active);
 -                              continue;
 -                      }
 +void handle_meta_connection_data(int fd, short events, void *data) {
 +      connection_t *c = data;
 +      int result;
 +      socklen_t len = sizeof result;
 +
 +      if(c->status.connecting) {
 +              c->status.connecting = false;
 +
 +              getsockopt(c->socket, SOL_SOCKET, SO_ERROR, &result, &len);
 +
 +              if(!result)
 +                      finish_connecting(c);
 +              else {
 +                      ifdebug(CONNECTIONS) logger(LOG_DEBUG,
 +                                         "Error while connecting to %s (%s): %s",
 +                                         c->name, c->hostname, sockstrerror(result));
 +                      closesocket(c->socket);
 +                      do_outgoing_connection(c);
 +                      return;
                }
        }
  
 -      for(i = 0; i < listen_sockets; i++) {
 -              if(FD_ISSET(listen_socket[i].udp, readset))
 -                      handle_incoming_vpn_data(listen_socket[i].udp);
 -
 -              if(FD_ISSET(listen_socket[i].tcp, readset))
 -                      handle_new_meta_connection(listen_socket[i].tcp);
 +      if (!receive_meta(c)) {
 +              terminate_connection(c, c->status.active);
 +              return;
        }
  }
  
 -/*
 -  this is where it all happens...
 -*/
 -int main_loop(void) {
 -      fd_set readset, writeset;
 -      struct timeval tv;
 -      int r, maxfd;
 -      time_t last_ping_check, last_config_check, last_graph_dump;
 -      event_t *event;
 -
 -      last_ping_check = now;
 -      last_config_check = now;
 -      last_graph_dump = now;
 -      
 -      srand(now);
 -
 -      running = true;
 +static void sigterm_handler(int signal, short events, void *data) {
 +      logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
 +      event_loopexit(NULL);
 +}
  
 -      while(running) {
 -              now = time(NULL);
 +static void sighup_handler(int signal, short events, void *data) {
 +      logger(LOG_NOTICE, "Got %s signal", strsignal(signal));
 +      reload_configuration();
 +}
  
 -      //      tv.tv_sec = 1 + (rand() & 7);   /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
 -              tv.tv_sec = 1;
 -              tv.tv_usec = 0;
 +int reload_configuration(void) {
 +      connection_t *c;
 +      splay_node_t *node, *next;
 +      char *fname;
 +      struct stat s;
 +      static time_t last_config_check = 0;
  
 -              maxfd = build_fdset(&readset, &writeset);
 +      /* Reread our own configuration file */
  
 -#ifdef HAVE_MINGW
 -              LeaveCriticalSection(&mutex);
 -#endif
 -              r = select(maxfd + 1, &readset, &writeset, NULL, &tv);
 -#ifdef HAVE_MINGW
 -              EnterCriticalSection(&mutex);
 -#endif
 +      exit_configuration(&config_tree);
 +      init_configuration(&config_tree);
  
 -              if(r < 0) {
 -                      if(!sockwouldblock(sockerrno)) {
 -                              logger(LOG_ERR, "Error while waiting for input: %s", sockstrerror(sockerrno));
 -                              dump_connections();
 -                              return 1;
 -                      }
 -              }
 -
 -              if(r > 0)
 -                      check_network_activity(&readset, &writeset);
 +      if(!read_server_config()) {
 +              logger(LOG_ERR, "Unable to reread configuration file, exitting.");
 +              event_loopexit(NULL);
 +              return EINVAL;
 +      }
  
 -              if(do_purge) {
 -                      purge();
 -                      do_purge = false;
 +      /* Close connections to hosts that have a changed or deleted host config file */
 +      
 +      for(node = connection_tree->head; node; node = next) {
 +              c = node->data;
 +              next = node->next;
 +              
 +              if(c->outgoing) {
 +                      free(c->outgoing->name);
 +                      if(c->outgoing->ai)
 +                              freeaddrinfo(c->outgoing->ai);
 +                      free(c->outgoing);
 +                      c->outgoing = NULL;
                }
 +              
 +              xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
 +              if(stat(fname, &s) || s.st_mtime > last_config_check)
 +                      terminate_connection(c, c->status.active);
 +              free(fname);
 +      }
  
 -              /* Let's check if everybody is still alive */
 -
 -              if(last_ping_check + pingtimeout < now) {
 -                      check_dead_connections();
 -                      last_ping_check = now;
 -
 -                      if(routing_mode == RMODE_SWITCH)
 -                              age_subnets();
 -
 -                      age_past_requests();
 -
 -                      /* Should we regenerate our key? */
 -
 -                      if(keyexpires < now) {
 -                              avl_node_t *node;
 -                              node_t *n;
 +      last_config_check = time(NULL);
  
 -                              ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
 +      /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
  
 -                              for(node = node_tree->head; node; node = node->next) {
 -                                      n = node->data;
 -                                      if(n->inkey) {
 -                                              free(n->inkey);
 -                                              n->inkey = NULL;
 -                                      }
 -                              }
 +      if(strictsubnets) {
 +              subnet_t *subnet;
  
 -                              send_key_changed(broadcast, myself);
 -                              keyexpires = now + keylifetime;
 -                      }
 -                      if(contradicting_del_edge && contradicting_add_edge) {
 -                              logger(LOG_WARNING, "Possible node with same Name as us!");
 -
 -                              if(rand() % 3 == 0) {
 -                                      logger(LOG_ERR, "Shutting down, check configuration of all nodes for duplicate Names!");
 -                                      running = false;
 -                                      break;
 -                              }
 -
 -                              contradicting_add_edge = 0;
 -                              contradicting_del_edge = 0;
 -                      }
 +              for(node = subnet_tree->head; node; node = node->next) {
 +                      subnet = node->data;
 +                      subnet->expires = 1;
                }
  
 -              if(sigalrm) {
 -                      avl_node_t *node;
 -                      logger(LOG_INFO, "Flushing event queue");
 -                      expire_events();
 -                      for(node = connection_tree->head; node; node = node->next) {
 -                              connection_t *c = node->data;
 -                              send_ping(c);
 +              load_all_subnets();
 +
 +              for(node = subnet_tree->head; node; node = next) {
 +                      next = node->next;
 +                      subnet = node->data;
 +                      if(subnet->expires == 1) {
 +                              send_del_subnet(broadcast, subnet);
 +                              if(subnet->owner->status.reachable)
 +                                      subnet_update(subnet->owner, subnet, false);
 +                              subnet_del(subnet->owner, subnet);
 +                      } else if(subnet->expires == -1) {
 +                              subnet->expires = 0;
 +                      } else {
 +                              send_add_subnet(broadcast, subnet);
 +                              if(subnet->owner->status.reachable)
 +                                      subnet_update(subnet->owner, subnet, true);
                        }
 -                      sigalrm = false;
 -              }
 -
 -              while((event = get_expired_event())) {
 -                      event->handler(event->data);
 -                      free_event(event);
                }
 +      }
  
 -              if(sighup) {
 -                      connection_t *c;
 -                      avl_node_t *node, *next;
 -                      char *fname;
 -                      struct stat s;
 -                      
 -                      sighup = false;
 -                      
 -                      /* Reread our own configuration file */
 -
 -                      exit_configuration(&config_tree);
 -                      init_configuration(&config_tree);
 -
 -                      if(!read_server_config()) {
 -                              logger(LOG_ERR, "Unable to reread configuration file, exitting.");
 -                              return 1;
 -                      }
 -
 -                      /* Cancel non-active outgoing connections */
 -
 -                      for(node = connection_tree->head; node; node = next) {
 -                              next = node->next;
 -                              c = node->data;
 -
 -                              c->outgoing = NULL;
 -
 -                              if(c->status.connecting) {
 -                                      terminate_connection(c, false);
 -                                      connection_del(c);
 -                              }
 -                      }
 -
 -                      /* Wipe list of outgoing connections */
 -
 -                      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);
 -
 -                      /* Close connections to hosts that have a changed or deleted host config file */
 -                      
 -                      for(node = connection_tree->head; node; node = node->next) {
 -                              c = node->data;
 -                              
 -                              xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
 -                              if(stat(fname, &s) || s.st_mtime > last_config_check)
 -                                      terminate_connection(c, c->status.active);
 -                              free(fname);
 -                      }
 +      /* Try to make outgoing connections */
 +      
 +      try_outgoing_connections();
  
 -                      last_config_check = now;
 +      return 0;
 +}
  
 -                      /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
 +void retry(void) {
 +      connection_t *c;
 +      splay_node_t *node;
  
 -                      if(strictsubnets) {
 -                              subnet_t *subnet;
 +      for(node = connection_tree->head; node; node = node->next) {
 +              c = node->data;
 +              
 +              if(c->outgoing && !c->node) {
 +                      if(timeout_initialized(&c->outgoing->ev))
 +                              event_del(&c->outgoing->ev);
 +                      if(c->status.connecting)
 +                              close(c->socket);
 +                      c->outgoing->timeout = 0;
 +                      do_outgoing_connection(c);
 +              }
 +      }
 +}
  
 -                              for(node = subnet_tree->head; node; node = node->next) {
 -                                      subnet = node->data;
 -                                      subnet->expires = 1;
 -                              }
 +/*
 +  this is where it all happens...
 +*/
 +int main_loop(void) {
 +      struct event timeout_event;
 +      struct event sighup_event;
 +      struct event sigterm_event;
 +      struct event sigquit_event;
  
 -                              load_all_subnets();
 -
 -                              for(node = subnet_tree->head; node; node = next) {
 -                                      next = node->next;
 -                                      subnet = node->data;
 -                                      if(subnet->expires == 1) {
 -                                              send_del_subnet(broadcast, subnet);
 -                                              if(subnet->owner->status.reachable)
 -                                                      subnet_update(subnet->owner, subnet, false);
 -                                              subnet_del(subnet->owner, subnet);
 -                                      } else if(subnet->expires == -1) {
 -                                              subnet->expires = 0;
 -                                      } else {
 -                                              send_add_subnet(broadcast, subnet);
 -                                              if(subnet->owner->status.reachable)
 -                                                      subnet_update(subnet->owner, subnet, true);
 -                                      }
 -                              }
 -                      }
 +      timeout_set(&timeout_event, timeout_handler, &timeout_event);
 +      event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
  
 -                      /* Try to make outgoing connections */
 -                      
 -                      try_outgoing_connections();
 -              }
 -              
 -              /* Dump graph if wanted every 60 seconds*/
 +#ifdef SIGHUP
 +      signal_set(&sighup_event, SIGHUP, sighup_handler, NULL);
 +      signal_add(&sighup_event, NULL);
 +#endif
 +#ifdef SIGTERM
 +      signal_set(&sigterm_event, SIGTERM, sigterm_handler, NULL);
 +      signal_add(&sigterm_event, NULL);
 +#endif
 +#ifdef SIGQUIT
 +      signal_set(&sigquit_event, SIGQUIT, sigterm_handler, NULL);
 +      signal_add(&sigquit_event, NULL);
 +#endif
  
 -              if(last_graph_dump + 60 < now) {
 -                      dump_graph();
 -                      last_graph_dump = now;
 -              }
 +      if(event_loop(0) < 0) {
 +              logger(LOG_ERR, "Error while waiting for input: %s", strerror(errno));
 +              return 1;
        }
  
 +      signal_del(&sighup_event);
 +      signal_del(&sigterm_event);
 +      signal_del(&sigquit_event);
 +      event_del(&timeout_event);
 +
        return 0;
  }
diff --combined src/net.h
index cd97e301e3e8f85c30d21c0df5f2e69a3de960cb,eae979cd1b72c59bf2d1a5b74a9792fb480842cd..f53c27a411896d5cc58ffcb30fc76bf72a350af5
+++ b/src/net.h
@@@ -21,9 -21,9 +21,9 @@@
  #ifndef __TINC_NET_H__
  #define __TINC_NET_H__
  
 -#include <openssl/evp.h>
 -
  #include "ipv6.h"
 +#include "cipher.h"
 +#include "digest.h"
  
  #ifdef ENABLE_JUMBOGRAMS
  #define MTU 9018                              /* 9000 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
  #define MTU 1518                              /* 1500 bytes payload + 14 bytes ethernet header + 4 bytes VLAN tag */
  #endif
  
 -#define MAXSIZE (MTU + 4 + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + MTU/64 + 20)      /* MTU + seqno + padding + HMAC + compressor overhead */
 +#define MAXSIZE (MTU + 4 + CIPHER_MAX_BLOCK_SIZE + DIGEST_MAX_SIZE + MTU/64 + 20)     /* MTU + seqno + padding + HMAC + compressor overhead */
  #define MAXBUFSIZE ((MAXSIZE > 2048 ? MAXSIZE : 2048) + 128)  /* Enough room for a request with a MAXSIZEd packet or a 8192 bits RSA key */
  
 -#define MAXSOCKETS 128                        /* Overkill... */
 +#define MAXSOCKETS 8                  /* Probably overkill... */
  
  typedef struct mac_t {
        uint8_t x[6];
@@@ -84,8 -84,6 +84,8 @@@ typedef struct vpn_packet_t 
  } vpn_packet_t;
  
  typedef struct listen_socket_t {
 +      struct event ev_tcp;
 +      struct event ev_udp;
        int tcp;
        int udp;
        sockaddr_t sa;
@@@ -100,7 -98,7 +100,7 @@@ typedef struct outgoing_t 
        struct config_t *cfg;
        struct addrinfo *ai;
        struct addrinfo *aip;
 -      struct event *event;
 +      struct event ev;
  } outgoing_t;
  
  extern list_t *outgoing_list;
@@@ -111,19 -109,24 +111,21 @@@ extern int addressfamily
  
  extern listen_socket_t listen_socket[MAXSOCKETS];
  extern int listen_sockets;
 -extern int keyexpires;
  extern int keylifetime;
  extern bool do_prune;
 -extern bool do_purge;
  extern char *myport;
 -extern time_t now;
+ extern int contradicting_add_edge;
+ extern int contradicting_del_edge;
  
  /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */
  #include "connection.h"
  #include "node.h"
  
  extern void retry_outgoing(outgoing_t *);
 -extern void handle_incoming_vpn_data(int);
 +extern void handle_incoming_vpn_data(int, short, void *);
  extern void finish_connecting(struct connection_t *);
  extern void do_outgoing_connection(struct connection_t *);
 -extern bool handle_new_meta_connection(int);
 +extern void handle_new_meta_connection(int, short, void *);
  extern int setup_listen_socket(const sockaddr_t *);
  extern int setup_vpn_in_socket(const sockaddr_t *);
  extern void send_packet(const struct node_t *, vpn_packet_t *);
@@@ -138,12 -141,6 +140,12 @@@ extern void terminate_connection(struc
  extern void flush_queue(struct node_t *);
  extern bool read_rsa_public_key(struct connection_t *);
  extern void send_mtu_probe(struct node_t *);
 +extern void handle_device_data(int, short, void *);
 +extern void handle_meta_connection_data(int, short, void *);
 +extern void regenerate_key();
 +extern void purge(void);
 +extern void retry(void);
 +extern int reload_configuration(void);
  extern void load_all_subnets();
  
  #ifndef HAVE_MINGW
diff --combined src/net_packet.c
index 46d4e1a0df5314f22d767f038d9e39cb02e3f126,44ab55d42c8a550f1fddad9a36c88aec0c813377..b444bc9310277605e4bec46108f25bd578eeba0f
@@@ -2,6 -2,7 +2,7 @@@
      net_packet.c -- Handles in- and outgoing VPN packets
      Copyright (C) 1998-2005 Ivo Timmermans,
                    2000-2010 Guus Sliepen <guus@tinc-vpn.org>
+                   2010      Timothy Redaelli <timothy@redaelli.eu>
  
      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
  #include LZO1X_H
  #endif
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "conf.h"
  #include "connection.h"
 +#include "crypto.h"
 +#include "digest.h"
  #include "device.h"
  #include "ethernet.h"
 -#include "event.h"
  #include "graph.h"
  #include "list.h"
  #include "logger.h"
@@@ -68,13 -67,13 +69,13 @@@ static void send_udppacket(node_t *, vp
  // mtuprobes ==    32: send 1 burst, sleep pingtimeout second
  // mtuprobes ==    33: no response from other side, restart PMTU discovery process
  
 -void send_mtu_probe(node_t *n) {
 +static void send_mtu_probe_handler(int fd, short events, void *data) {
 +      node_t *n = data;
        vpn_packet_t packet;
        int len, i;
        int timeout = 1;
        
        n->mtuprobes++;
 -      n->mtuevent = NULL;
  
        if(!n->status.reachable || !n->status.validkey) {
                ifdebug(TRAFFIC) logger(LOG_INFO, "Trying to send MTU probe to unreachable or rekeying node %s (%s)", n->name, n->hostname);
                        len = 64;
                
                memset(packet.data, 0, 14);
 -              RAND_pseudo_bytes(packet.data + 14, len - 14);
 +              randomize(packet.data + 14, len - 14);
                packet.len = len;
                packet.priority = 0;
  
        }
  
  end:
 -      n->mtuevent = new_event();
 -      n->mtuevent->handler = (event_handler_t)send_mtu_probe;
 -      n->mtuevent->data = n;
 -      n->mtuevent->time = now + timeout;
 -      event_add(n->mtuevent);
 +      event_add(&n->mtuevent, &(struct timeval){timeout, 0});
 +}
 +
 +void send_mtu_probe(node_t *n) {
 +      if(!timeout_initialized(&n->mtuevent))
 +              timeout_set(&n->mtuevent, send_mtu_probe_handler, n);
 +      send_mtu_probe_handler(0, 0, n);
  }
  
  void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
@@@ -225,11 -222,15 +226,11 @@@ static void receive_packet(node_t *n, v
        route(n, packet);
  }
  
 -static bool try_mac(const node_t *n, const vpn_packet_t *inpkt) {
 -      unsigned char hmac[EVP_MAX_MD_SIZE];
 -
 -      if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
 +static bool try_mac(node_t *n, const vpn_packet_t *inpkt) {
 +      if(!digest_active(&n->indigest) || inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest))
                return false;
  
 -      HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL);
 -
 -      return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength);
 +      return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len - n->indigest.maclength, (const char *)&inpkt->seqno + inpkt->len - n->indigest.maclength);
  }
  
  static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
        vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
        int nextpkt = 0;
        vpn_packet_t *outpkt = pkt[0];
 -      int outlen, outpad;
 -      unsigned char hmac[EVP_MAX_MD_SIZE];
 +      size_t outlen;
        int i;
  
 -      if(!n->inkey) {
 +      if(!cipher_active(&n->incipher)) {
                ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got packet from %s (%s) but he hasn't got our key yet",
                                        n->name, n->hostname);
                return;
  
        /* Check packet length */
  
 -      if(inpkt->len < sizeof(inpkt->seqno) + n->inmaclength) {
 +      if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
                ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got too short packet from %s (%s)",
                                        n->name, n->hostname);
                return;
  
        /* Check the message authentication code */
  
 -      if(n->indigest && n->inmaclength) {
 -              inpkt->len -= n->inmaclength;
 -              HMAC(n->indigest, n->inkey, n->inkeylength,
 -                       (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL);
 -
 -              if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) {
 -                      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)",
 -                                         n->name, n->hostname);
 +      if(digest_active(&n->indigest)) {
 +              inpkt->len -= n->indigest.maclength;
 +              if(!digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, (const char *)&inpkt->seqno + inpkt->len)) {
 +                      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Got unauthenticated packet from %s (%s)", n->name, n->hostname);
                        return;
                }
        }
 -
        /* Decrypt the packet */
  
 -      if(n->incipher) {
 +      if(cipher_active(&n->incipher)) {
                outpkt = pkt[nextpkt++];
 +              outlen = MAXSIZE;
  
 -              if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL)
 -                              || !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen,
 -                                      (unsigned char *) &inpkt->seqno, inpkt->len)
 -                              || !EVP_DecryptFinal_ex(&n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
 -                      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s): %s",
 -                                              n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
 +              if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
 +                      ifdebug(TRAFFIC) logger(LOG_DEBUG, "Error decrypting packet from %s (%s)", n->name, n->hostname);
                        return;
                }
                
 -              outpkt->len = outlen + outpad;
 +              outpkt->len = outlen;
                inpkt = outpkt;
        }
  
        /* Check the sequence number */
  
 -      inpkt->len -= sizeof(inpkt->seqno);
 +      inpkt->len -= sizeof inpkt->seqno;
        inpkt->seqno = ntohl(inpkt->seqno);
  
        if(inpkt->seqno != n->received_seqno + 1) {
 -              if(inpkt->seqno >= n->received_seqno + sizeof(n->late) * 8) {
 +              if(inpkt->seqno >= n->received_seqno + sizeof n->late * 8) {
                        logger(LOG_WARNING, "Lost %d packets from %s (%s)",
                                           inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
                        
 -                      memset(n->late, 0, sizeof(n->late));
 +                      memset(n->late, 0, sizeof n->late);
                } else if (inpkt->seqno <= n->received_seqno) {
 -                      if((n->received_seqno >= sizeof(n->late) * 8 && inpkt->seqno <= n->received_seqno - sizeof(n->late) * 8) || !(n->late[(inpkt->seqno / 8) % sizeof(n->late)] & (1 << inpkt->seqno % 8))) {
 +                      if((n->received_seqno >= sizeof n->late * 8 && inpkt->seqno <= n->received_seqno - sizeof n->late * 8) || !(n->late[(inpkt->seqno / 8) % sizeof n->late] & (1 << inpkt->seqno % 8))) {
                                logger(LOG_WARNING, "Got late or replayed packet from %s (%s), seqno %d, last received %d",
                                           n->name, n->hostname, inpkt->seqno, n->received_seqno);
                                return;
                        }
                } else {
                        for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
 -                              n->late[(i / 8) % sizeof(n->late)] |= 1 << i % 8;
 +                              n->late[(i / 8) % sizeof n->late] |= 1 << i % 8;
                }
        }
        
 -      n->late[(inpkt->seqno / 8) % sizeof(n->late)] &= ~(1 << inpkt->seqno % 8);
 +      n->late[(inpkt->seqno / 8) % sizeof n->late] &= ~(1 << inpkt->seqno % 8);
  
        if(inpkt->seqno > n->received_seqno)
                n->received_seqno = inpkt->seqno;
                        
        if(n->received_seqno > MAX_SEQNO)
 -              keyexpires = 0;
 +              regenerate_key();
  
        /* Decompress the packet */
  
@@@ -355,8 -365,10 +356,10 @@@ static void send_udppacket(node_t *n, v
        int nextpkt = 0;
        vpn_packet_t *outpkt;
        int origlen;
 -      int outlen, outpad;
 +      size_t outlen;
+ #if defined(SOL_IP) && defined(IP_TOS)
        static int priority = 0;
+ #endif
        int origpriority;
        int sock;
  
        /* Make sure we have a valid key */
  
        if(!n->status.validkey) {
 +              time_t now = time(NULL);
 +
                ifdebug(TRAFFIC) logger(LOG_INFO,
                                   "No valid key known yet for %s (%s), forwarding via TCP",
                                   n->name, n->hostname);
        /* Add sequence number */
  
        inpkt->seqno = htonl(++(n->sent_seqno));
 -      inpkt->len += sizeof(inpkt->seqno);
 +      inpkt->len += sizeof inpkt->seqno;
  
        /* Encrypt the packet */
  
 -      if(n->outcipher) {
 +      if(cipher_active(&n->outcipher)) {
                outpkt = pkt[nextpkt++];
 +              outlen = MAXSIZE;
  
 -              if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL)
 -                              || !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen,
 -                                      (unsigned char *) &inpkt->seqno, inpkt->len)
 -                              || !EVP_EncryptFinal_ex(&n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
 -                      ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s): %s",
 -                                              n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
 +              if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
 +                      ifdebug(TRAFFIC) logger(LOG_ERR, "Error while encrypting packet to %s (%s)", n->name, n->hostname);
                        goto end;
                }
  
 -              outpkt->len = outlen + outpad;
 +              outpkt->len = outlen;
                inpkt = outpkt;
        }
  
        /* Add the message authentication code */
  
 -      if(n->outdigest && n->outmaclength) {
 -              HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno,
 -                       inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL);
 -              inpkt->len += n->outmaclength;
 +      if(digest_active(&n->outdigest)) {
 +              digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, (char *)&inpkt->seqno + inpkt->len);
 +              inpkt->len += digest_length(&n->outdigest);
        }
  
        /* Determine which socket we have to use */
           && listen_socket[sock].sa.sa.sa_family == AF_INET) {
                priority = origpriority;
                ifdebug(TRAFFIC) logger(LOG_DEBUG, "Setting outgoing packet priority to %d", priority);
 -              if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof(priority)))    /* SO_PRIORITY doesn't seem to work */
 +              if(setsockopt(listen_socket[sock].udp, SOL_IP, IP_TOS, &priority, sizeof priority))     /* SO_PRIORITY doesn't seem to work */
                        logger(LOG_ERR, "System call `%s' failed: %s", "setsockopt", strerror(errno));
        }
  #endif
@@@ -514,7 -528,7 +517,7 @@@ void send_packet(const node_t *n, vpn_p
  /* Broadcast a packet using the minimum spanning tree */
  
  void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
 -      avl_node_t *node;
 +      splay_node_t *node;
        connection_t *c;
  
        ifdebug(TRAFFIC) logger(LOG_INFO, "Broadcasting packet of %d bytes from %s (%s)",
  }
  
  static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
 -      avl_node_t *node;
 -      edge_t *e;
 -      node_t *n = NULL;
 +      splay_node_t *node;
 +      node_t *n, *found = NULL;
        static time_t last_hard_try = 0;
 +      time_t now = time(NULL);
  
 -      for(node = edge_weight_tree->head; node; node = node->next) {
 -              e = node->data;
 -
 -              if(sockaddrcmp_noport(from, &e->address)) {
 -                      if(last_hard_try == now)
 -                              continue;
 -                      last_hard_try = now;
 -              }
 +      if(last_hard_try == now)
 +              return NULL;
 +      else
 +              last_hard_try = now;
  
 -              if(!n)
 -                      n = e->to;
 +      for(node = node_tree->head; node; node = node->next) {
 +              n = node->data;
  
 -              if(!try_mac(e->to, pkt))
 +              if(n == myself || !n->status.reachable || !digest_active(&n->indigest))
                        continue;
  
 -              n = e->to;
 -              break;
 +              if(try_mac(n, pkt)) {
 +                      found = n;
 +                      break;
 +              }
        }
  
 -      return n;
 +      return found;
  }
  
 -void handle_incoming_vpn_data(int sock) {
 +void handle_incoming_vpn_data(int sock, short events, void *data) {
        vpn_packet_t pkt;
        char *hostname;
        sockaddr_t from;
 -      socklen_t fromlen = sizeof(from);
 +      socklen_t fromlen = sizeof from;
        node_t *n;
 +      int len;
  
 -      pkt.len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
 +      len = recvfrom(sock, (char *) &pkt.seqno, MAXSIZE, 0, &from.sa, &fromlen);
  
 -      if(pkt.len < 0) {
 +      if(len <= 0 || len > MAXSIZE) {
                if(!sockwouldblock(sockerrno))
                        logger(LOG_ERR, "Receiving packet failed: %s", sockstrerror(sockerrno));
                return;
        }
  
 +      pkt.len = len;
 +
        sockaddrunmap(&from);           /* Some braindead IPv6 implementations do stupid things. */
  
        n = lookup_node_udp(&from);
  
        receive_udppacket(n, &pkt);
  }
 +
 +void handle_device_data(int sock, short events, void *data) {
 +      vpn_packet_t packet;
 +
 +      if(read_packet(&packet))
 +              route(myself, &packet);
 +}
diff --combined src/net_setup.c
index 198da3df93419e7760d58d98a11c8dca14b17923,f4e56378889c09c1d008cf51e05cb05fdb43b48c..9c188957afa449c5fca8849f69d0884893cae642
  
  #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 "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;
  
  bool read_rsa_public_key(connection_t *c) {
        FILE *fp;
        char *fname;
 -      char *key;
 -
 -      if(!c->rsa_key) {
 -              c->rsa_key = RSA_new();
 -//            RSA_blinding_on(c->rsa_key, NULL);
 -      }
 +      char *n;
 +      bool result;
  
        /* First, check for simple PublicKey statement */
  
 -      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;
 +      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)) {
 -              fp = fopen(fname, "r");
 -
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 -
 -              free(fname);
 -              c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 -
 -              if(c->rsa_key)
 -                      return true;            /* Woohoo. */
 -
 -              /* If it fails, try PEM_read_RSA_PUBKEY. */
 -              fp = fopen(fname, "r");
 +      if(!get_config_string(lookup_config(c->config_tree, "PublicKeyFile"), &fname))
 +              xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
  
 -              if(!fp) {
 -                      logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
 -                                 fname, strerror(errno));
 -                      free(fname);
 -                      return false;
 -              }
 -
 -              free(fname);
 -              c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 -
 -              if(c->rsa_key) {
 -//                            RSA_blinding_on(c->rsa_key, NULL);
 -                      return true;
 -              }
 +      fp = fopen(fname, "r");
  
 -              logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s",
 +      if(!fp) {
 +              logger(LOG_ERR, "Error reading RSA public key file `%s': %s",
                           fname, strerror(errno));
 +              free(fname);
                return false;
        }
  
 -      /* Else, check if a harnessed public key is in the config file */
 -
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
 -      fp = fopen(fname, "r");
 -
 -      if(fp) {
 -              c->rsa_key = PEM_read_RSAPublicKey(fp, &c->rsa_key, NULL, NULL);
 -              fclose(fp);
 -      }
 -
 -      free(fname);
 -
 -      if(c->rsa_key)
 -              return true;
 -
 -      /* Try again with PEM_read_RSA_PUBKEY. */
 -
 -      xasprintf(&fname, "%s/hosts/%s", confbase, c->name);
 -      fp = fopen(fname, "r");
 -
 -      if(fp) {
 -              c->rsa_key = PEM_read_RSA_PUBKEY(fp, &c->rsa_key, NULL, NULL);
 -//            RSA_blinding_on(c->rsa_key, NULL);
 -              fclose(fp);
 -      }
 +      result = rsa_read_pem_public_key(&c->rsa, fp);
 +      fclose(fp);
  
 +      if(!result) 
 +              logger(LOG_ERR, "Reading RSA public key file `%s' failed: %s", fname, strerror(errno));
        free(fname);
 -
 -      if(c->rsa_key)
 -              return true;
 -
 -      logger(LOG_ERR, "No public key for %s specified!", c->name);
 -
 -      return false;
 +      return result;
  }
  
 -bool read_rsa_private_key(void) {
 +bool read_rsa_private_key() {
        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)) {
 +      /* First, check for simple PrivateKey statement */
 +
 +      if(get_config_string(lookup_config(config_tree, "PrivateKey"), &d)) {
-               if(!get_config_string(lookup_config(myself->connection->config_tree, "PublicKey"), &n)) {
++              if(!get_config_string(lookup_config(config_tree, "PublicKey"), &n)) {
                        logger(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);
  
        }
  
  #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(LOG_ERR, "Could not stat RSA private key file `%s': %s'", fname, strerror(errno));
                free(fname);
                return false;
        }
                logger(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(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() {
 +      if(timeout_initialized(&keyexpire_event)) {
 +              ifdebug(STATUS) logger(LOG_INFO, "Expiring symmetric keys");
 +              event_del(&keyexpire_event);
 +              send_key_changed(broadcast, myself);
 +      } else {
 +              timeout_set(&keyexpire_event, keyexpire_handler, NULL);
        }
  
 -      free(fname);
 -      return true;
 +      event_add(&keyexpire_event, &(struct timeval){keylifetime, 0});
  }
  
  /*
@@@ -162,7 -209,7 +162,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;
@@@ -223,6 -270,7 +223,7 @@@ bool setup_myself(void) 
        config_t *cfg;
        subnet_t *subnet;
        char *name, *hostname, *mode, *afname, *cipher, *digest;
+       char *fname = NULL;
        char *address = NULL;
        char *envp[5];
        struct addrinfo *ai, *aip, hint = {0};
  
        myself = new_node();
        myself->connection = new_connection();
-       init_configuration(&myself->connection->config_tree);
  
        myself->hostname = xstrdup("MYSELF");
        myself->connection->hostname = xstrdup("MYSELF");
  
        myself->name = name;
        myself->connection->name = xstrdup(name);
-       if(!read_connection_config(myself->connection)) {
-               logger(LOG_ERR, "Cannot open host configuration file for myself!");
-               return false;
-       }
+       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)
-                       && !get_config_string(lookup_config(myself->connection->config_tree, "Port"), &myport))
+       if(!get_config_string(lookup_config(config_tree, "Port"), &myport))
                myport = xstrdup("655");
  
        if(!atoi(myport)) {
  
        /* Read in all the subnets specified in the host configuration file */
  
-       cfg = lookup_config(myself->connection->config_tree, "Subnet");
+       cfg = lookup_config(config_tree, "Subnet");
  
        while(cfg) {
                if(!get_config_subnet(cfg, &subnet))
  
                subnet_add(myself, subnet);
  
-               cfg = lookup_config_next(myself->connection->config_tree, cfg);
+               cfg = lookup_config_next(config_tree, cfg);
        }
  
        /* Check some options */
        if(get_config_bool(lookup_config(config_tree, "TCPOnly"), &choice) && choice)
                myself->options |= OPTION_TCPONLY;
  
-       if(get_config_bool(lookup_config(myself->connection->config_tree, "IndirectData"), &choice) && choice)
-               myself->options |= OPTION_INDIRECT;
-       if(get_config_bool(lookup_config(myself->connection->config_tree, "TCPOnly"), &choice) && choice)
-               myself->options |= OPTION_TCPONLY;
        if(myself->options & OPTION_TCPONLY)
                myself->options |= OPTION_INDIRECT;
  
        }
  
        choice = true;
-       get_config_bool(lookup_config(myself->connection->config_tree, "PMTUDiscovery"), &choice);
        get_config_bool(lookup_config(config_tree, "PMTUDiscovery"), &choice);
        if(choice)
                myself->options |= OPTION_PMTU_DISCOVERY;
  
        choice = true;
        get_config_bool(lookup_config(config_tree, "ClampMSS"), &choice);
-       get_config_bool(lookup_config(myself->connection->config_tree, "ClampMSS"), &choice);
        if(choice)
                myself->options |= OPTION_CLAMP_MSS;
  
  
        /* Generate packet encryption key */
  
-       if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
 -      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(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);
 +      if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
 +              digest = xstrdup("sha1");
  
 -                      if(!myself->indigest) {
 -                              logger(LOG_ERR, "Unrecognized digest type!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->indigest = EVP_sha1();
 -
 -      myself->connection->outdigest = EVP_sha1();
 -
 -      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;
 +      int maclength = 4;
-       get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &maclength);
++      get_config_int(lookup_config(config_tree, "MACLength"), &maclength);
 +
 +      if(maclength < 0) {
 +              logger(LOG_ERR, "Bogus MAC length!");
 +              return false;
 +      }
  
 -      myself->connection->outmaclength = 0;
 +      if(!digest_open_by_name(&myself->indigest, digest, maclength)) {
 +              logger(LOG_ERR, "Unrecognized digest type!");
 +              return false;
 +      }
  
        /* Compression */
  
-       if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->incompression)) {
+       if(get_config_int(lookup_config(config_tree, "Compression"), &myself->incompression)) {
                if(myself->incompression < 0 || myself->incompression > 11) {
                        logger(LOG_ERR, "Bogus compression level!");
                        return false;
        if(!setup_device())
                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(LOG_ERR, "event_add failed: %s", strerror(errno));
 +                      close_device();
 +                      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 */
                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;
 +              }
 +
 +              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(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, NULL);
 +              if(event_add(&listen_socket[listen_sockets].ev_udp, NULL) < 0) {
 +                      logger(LOG_ERR, "event_add failed: %s", strerror(errno));
 +                      abort();
 +              }
  
                ifdebug(CONNECTIONS) {
                        hostname = sockaddr2hostname((sockaddr_t *) aip->ai_addr);
  
                memcpy(&listen_socket[listen_sockets].sa, aip->ai_addr, aip->ai_addrlen);
                listen_sockets++;
 +
 +              if(listen_sockets >= MAXSOCKETS) {
 +                      logger(LOG_WARNING, "Maximum of %d listening sockets reached", MAXSOCKETS);
 +                      break;
 +              }
        }
  
        freeaddrinfo(ai);
    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;
        for(node = connection_tree->head; node; node = next) {
                next = node->next;
                c = node->data;
 -              c->outgoing = NULL;
 +              c->outgoing = false;
                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/net_socket.c
index 4fe65161d5b7595e097772320229128a9880100a,762c0a224d0561976079c803fcd85a8fcc3f9cd6..44d7f77159c5d586ceb8fdb62438e5f6c5593538
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
  #include "conf.h"
  #include "connection.h"
 -#include "event.h"
  #include "logger.h"
  #include "meta.h"
  #include "net.h"
@@@ -69,12 -70,12 +69,12 @@@ static void configure_tcp(connection_t 
  
  #if defined(SOL_TCP) && defined(TCP_NODELAY)
        option = 1;
-       setsockopt(c->socket, SOL_TCP, TCP_NODELAY, &option, sizeof option);
 -      setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof(option));
++      setsockopt(c->socket, SOL_TCP, TCP_NODELAY, (void *)&option, sizeof option);
  #endif
  
  #if defined(SOL_IP) && defined(IP_TOS) && defined(IPTOS_LOWDELAY)
        option = IPTOS_LOWDELAY;
-       setsockopt(c->socket, SOL_IP, IP_TOS, &option, sizeof option);
 -      setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof(option));
++      setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof option);
  #endif
  }
  
@@@ -94,7 -95,7 +94,7 @@@ static bool bind_to_interface(int sd) 
        strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
        ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
  
-       status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
+       status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr));
        if(status) {
                logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
                                strerror(errno));
@@@ -180,11 -181,11 +180,11 @@@ int setup_listen_socket(const sockaddr_
        /* Optimize TCP settings */
  
        option = 1;
-       setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option);
 -      setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
++      setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
  
  #if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
        if(sa->sa.sa_family == AF_INET6)
-               setsockopt(nfd, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option);
+               setsockopt(nfd, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
  #endif
  
        if(get_config_string
  #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
                struct ifreq ifr;
  
 -              memset(&ifr, 0, sizeof(ifr));
 +              memset(&ifr, 0, sizeof ifr);
                strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
  
-               if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) {
 -              if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr))) {
++              if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof ifr)) {
                        closesocket(nfd);
                        logger(LOG_ERR, "Can't bind to interface %s: %s", iface,
                                   strerror(sockerrno));
@@@ -258,11 -259,11 +258,11 @@@ int setup_vpn_in_socket(const sockaddr_
  #endif
  
        option = 1;
-       setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option);
 -      setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
++      setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
  
  #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
        if(sa->sa.sa_family == AF_INET6)
-               setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, &option, sizeof option);
+               setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
  #endif
  
  #if defined(IP_DONTFRAG) && !defined(IP_DONTFRAGMENT)
  #if defined(SOL_IP) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
        if(myself->options & OPTION_PMTU_DISCOVERY) {
                option = IP_PMTUDISC_DO;
-               setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, &option, sizeof(option));
+               setsockopt(nfd, SOL_IP, IP_MTU_DISCOVER, (void *)&option, sizeof(option));
        }
  #elif defined(IPPROTO_IP) && defined(IP_DONTFRAGMENT)
        if(myself->options & OPTION_PMTU_DISCOVERY) {
                option = 1;
-               setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, &option, sizeof(option));
+               setsockopt(nfd, IPPROTO_IP, IP_DONTFRAGMENT, (void *)&option, sizeof(option));
        }
  #else
  #warning No way to disable IPv4 fragmentation
  #if defined(SOL_IPV6) && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
        if(myself->options & OPTION_PMTU_DISCOVERY) {
                option = IPV6_PMTUDISC_DO;
-               setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, &option, sizeof(option));
+               setsockopt(nfd, SOL_IPV6, IPV6_MTU_DISCOVER, (void *)&option, sizeof(option));
        }
  #elif defined(IPPROTO_IPV6) && defined(IPV6_DONTFRAG)
        if(myself->options & OPTION_PMTU_DISCOVERY) {
                option = 1;
-               setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, &option, sizeof(option));
+               setsockopt(nfd, IPPROTO_IPV6, IPV6_DONTFRAG, (void *)&option, sizeof(option));
        }
  #else
  #warning No way to disable IPv6 fragmentation
        return nfd;
  } /* int setup_vpn_in_socket */
  
 +static void retry_outgoing_handler(int fd, short events, void *data) {
 +      setup_outgoing_connection(data);
 +}
 +
  void retry_outgoing(outgoing_t *outgoing) {
        outgoing->timeout += 5;
  
        if(outgoing->timeout > maxtimeout)
                outgoing->timeout = maxtimeout;
  
 -      if(outgoing->event)
 -              event_del(outgoing->event);
 -      outgoing->event = new_event();
 -      outgoing->event->handler = (event_handler_t) setup_outgoing_connection;
 -      outgoing->event->time = now + outgoing->timeout;
 -      outgoing->event->data = outgoing;
 -      event_add(outgoing->event);
 +      timeout_set(&outgoing->ev, retry_outgoing_handler, outgoing);
 +      event_add(&outgoing->ev, &(struct timeval){outgoing->timeout, 0});
  
        ifdebug(CONNECTIONS) logger(LOG_NOTICE,
                           "Trying to re-establish outgoing connection in %d seconds",
@@@ -336,8 -338,7 +336,8 @@@ void finish_connecting(connection_t *c
  
        configure_tcp(c);
  
 -      c->last_ping_time = now;
 +      c->last_ping_time = time(NULL);
 +      c->status.connecting = false;
  
        send_id(c);
  }
@@@ -356,9 -357,9 +356,9 @@@ begin
                if(!c->outgoing->cfg) {
                        ifdebug(CONNECTIONS) logger(LOG_ERR, "Could not set up a meta connection to %s",
                                           c->name);
 -                      c->status.remove = true;
                        retry_outgoing(c->outgoing);
                        c->outgoing = NULL;
 +                      connection_del(c);
                        return;
                }
  
  #if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
        int option = 1;
        if(c->address.sa.sa_family == AF_INET6)
-               setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option);
+               setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
  #endif
  
        bind_to_interface(c->socket);
        return;
  }
  
 +void handle_meta_read(struct bufferevent *event, void *data) {
 +      logger(LOG_ERR, "handle_meta_read() called");
 +      abort();
 +}
 +
 +void handle_meta_write(struct bufferevent *event, void *data) {
 +      ifdebug(META) logger(LOG_DEBUG, "handle_meta_write() called");
 +}
 +
 +void handle_meta_connection_error(struct bufferevent *event, short what, void *data) {
 +      connection_t *c = data;
 +      logger(LOG_ERR, "handle_meta_connection_error() called: %d: %s", what, strerror(errno));
 +      terminate_connection(c, c->status.active);
 +}
 +
  void setup_outgoing_connection(outgoing_t *outgoing) {
        connection_t *c;
        node_t *n;
  
 -      outgoing->event = NULL;
 +      event_del(&outgoing->ev);
  
        n = lookup_node(outgoing->name);
  
        }
  
        c->outgoing = outgoing;
 -      c->last_ping_time = now;
 +      c->last_ping_time = time(NULL);
  
        connection_add(c);
  
        do_outgoing_connection(c);
 +
 +      event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
 +      event_add(&c->inevent, NULL);
 +      c->buffer = bufferevent_new(c->socket, handle_meta_read, handle_meta_write, handle_meta_connection_error, c);
 +      if(!c->buffer) {
 +              logger(LOG_ERR, "bufferevent_new() failed: %s", strerror(errno));
 +              abort();
 +      }
 +      bufferevent_disable(c->buffer, EV_READ);
  }
  
  /*
    accept a new tcp connect and create a
    new connection
  */
 -bool handle_new_meta_connection(int sock) {
 +void handle_new_meta_connection(int sock, short events, void *data) {
        connection_t *c;
        sockaddr_t sa;
        int fd;
 -      socklen_t len = sizeof(sa);
 +      socklen_t len = sizeof sa;
  
        fd = accept(sock, &sa.sa, &len);
  
        if(fd < 0) {
                logger(LOG_ERR, "Accepting a new connection failed: %s", sockstrerror(sockerrno));
 -              return false;
 +              return;
        }
  
        sockaddrunmap(&sa);
        c->address = sa;
        c->hostname = sockaddr2hostname(&sa);
        c->socket = fd;
 -      c->last_ping_time = now;
 +      c->last_ping_time = time(NULL);
  
        ifdebug(CONNECTIONS) logger(LOG_NOTICE, "Connection from %s", c->hostname);
  
 +      event_set(&c->inevent, c->socket, EV_READ | EV_PERSIST, handle_meta_connection_data, c);
 +      event_add(&c->inevent, NULL);
 +      c->buffer = bufferevent_new(c->socket, NULL, handle_meta_write, handle_meta_connection_error, c);
 +      if(!c->buffer) {
 +              logger(LOG_ERR, "bufferevent_new() failed: %s", strerror(errno));
 +              abort();
 +      }
 +      bufferevent_disable(c->buffer, EV_READ);
 +              
        configure_tcp(c);
  
        connection_add(c);
  
        c->allow_request = ID;
        send_id(c);
 -
 -      return true;
  }
  
  void free_outgoing(outgoing_t *outgoing) {
@@@ -585,7 -555,7 +585,7 @@@ void try_outgoing_connections(void) 
                        continue;
                }
  
 -              outgoing = xmalloc_and_zero(sizeof(*outgoing));
 +              outgoing = xmalloc_and_zero(sizeof *outgoing);
                outgoing->name = name;
                list_insert_tail(outgoing_list, outgoing);
                setup_outgoing_connection(outgoing);
diff --combined src/process.c
index 09fd63e1071a0da957a44d9eff04acb1f16b72b1,f2fff1dc6d0446c0ab36c66921b74e26995dcc99..77454f755d05dc46481ee8cf7ca735d407302e90
  
  #include "conf.h"
  #include "connection.h"
 +#include "control.h"
  #include "device.h"
  #include "edge.h"
  #include "logger.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;
 -extern volatile bool running;
  
+ #ifndef HAVE_MINGW
  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);
@@@ -164,11 -171,19 +166,11 @@@ DWORD WINAPI controlhandler(DWORD reque
                        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) {
@@@ -225,13 -240,86 +227,13 @@@ bool init_service(void) 
  }
  #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 */
 -
        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());
@@@ -340,6 -435,22 +342,6 @@@ bool execute_script(const char *name, c
  */
  
  #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));
@@@ -360,7 -471,7 +362,7 @@@ static RETSIGTYPE fatal_signal_handler(
  
                close_network_connections();
                sleep(5);
 -              remove_pid(pidfilename);
 +              exit_control();
                execvp(g_argv[0], g_argv);
        } else {
                logger(LOG_NOTICE, "Not restarting.");
        }
  }
  
 -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) {
 -      dump_device_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));
  }
@@@ -380,11 -533,19 +382,11 @@@ 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},
        {0, NULL}
  };
  #endif
@@@ -411,7 -572,7 +413,7 @@@ void setup_signals(void) 
  
        /* If we didn't detach, allow coredumps */
        if(!do_detach)
 -              sighandlers[3].handler = SIG_DFL;
 +              sighandlers[0].handler = SIG_DFL;
  
        /* Then, for each known signal that we want to catch, assign a
           handler to the signal, with error checking this time. */
diff --combined src/process.h
index 740f7c7c80ff594006b24038e6373a14e6e70ac9,8d2d56211323c577b7d742a14637ce04a37f287b..ea7815eb538669086d5f550f0c410d967a51d57a
@@@ -22,6 -22,7 +22,6 @@@
  #define __TINC_PROCESS_H__
  
  extern bool do_detach;
 -extern bool sighup;
  extern bool sigalrm;
  
  extern void setup_signals(void);
@@@ -29,4 -30,8 +29,8 @@@ extern bool execute_script(const char *
  extern bool detach(void);
  extern bool kill_other(int);
  
+ #ifdef HAVE_MINGW
+ extern bool init_service(void);
+ #endif
  #endif                                                        /* __TINC_PROCESS_H__ */
diff --combined src/protocol_auth.c
index ca9e86b8a293d704331ac4eb9554d4d45673a45a,3f4fa01021a8db3a3ee7b7bbcea43720950ae3d4..6059096191101936a668274d0a92a39eabaf543f
  
  #include "system.h"
  
 -#include <openssl/sha.h>
 -#include <openssl/rand.h>
 -#include <openssl/err.h>
 -#include <openssl/evp.h>
 -
 -#include "avl_tree.h"
 +#include "splay_tree.h"
  #include "conf.h"
  #include "connection.h"
 +#include "control.h"
 +#include "control_common.h"
 +#include "crypto.h"
  #include "edge.h"
  #include "graph.h"
  #include "logger.h"
  #include "netutl.h"
  #include "node.h"
  #include "protocol.h"
 +#include "rsa.h"
  #include "utils.h"
  #include "xalloc.h"
  
  bool send_id(connection_t *c) {
 +      gettimeofday(&c->start, NULL);
 +
        return send_request(c, "%d %s %d", ID, myself->connection->name,
                                                myself->connection->protocol_version);
  }
  
 -bool id_h(connection_t *c) {
 +bool id_h(connection_t *c, char *request) {
        char name[MAX_STRING_SIZE];
  
 -      if(sscanf(c->buffer, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
 +      if(sscanf(request, "%*d " MAX_STRING " %d", name, &c->protocol_version) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ID", c->name,
                           c->hostname);
                return false;
        }
  
 +      /* Check if this is a control connection */
 +
 +      if(name[0] == '^' && !strcmp(name + 1, controlcookie)) {
 +              c->status.control = true;
 +              c->allow_request = CONTROL;
 +              c->last_ping_time = time(NULL) + 3600;
 +              return send_request(c, "%d %d %d", ACK, TINC_CTL_VERSION_CURRENT, getpid());
 +      }
 +
        /* Check if identity is a valid name */
  
        if(!check_id(name)) {
  }
  
  bool send_metakey(connection_t *c) {
 -      char *buffer;
 -      int len;
 -      bool x;
 +      size_t len = rsa_size(&c->rsa);
 +      char key[len];
 +      char enckey[len];
 +      char hexkey[2 * len + 1];
  
 -      len = RSA_size(c->rsa_key);
 -
 -      /* Allocate buffers for the meta key */
 -
 -      buffer = alloca(2 * len + 1);
 +      if(!cipher_open_blowfish_ofb(&c->outcipher))
 +              return false;
        
 -      c->outkey = xrealloc(c->outkey, len);
 -
 -      if(!c->outctx)
 -              c->outctx = xmalloc_and_zero(sizeof(*c->outctx));
 +      if(!digest_open_sha1(&c->outdigest, -1))
 +              return false;
  
 -      /* Copy random data to the buffer */
 +      /* Create a random key */
  
 -      RAND_pseudo_bytes((unsigned char *)c->outkey, len);
 +      randomize(key, len);
  
        /* The message we send must be smaller than the modulus of the RSA key.
           By definition, for a key of k bits, the following formula holds:
           This can be done by setting the most significant bit to zero.
         */
  
 -      c->outkey[0] &= 0x7F;
 +      key[0] &= 0x7F;
 +
 +      cipher_set_key_from_rsa(&c->outcipher, key, len, true);
  
        ifdebug(SCARY_THINGS) {
 -              bin2hex(c->outkey, buffer, len);
 -              buffer[len * 2] = '\0';
 -              logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s",
 -                         buffer);
 +              bin2hex(key, hexkey, len);
 +              hexkey[len * 2] = '\0';
 +              logger(LOG_DEBUG, "Generated random meta key (unencrypted): %s", hexkey);
        }
  
        /* Encrypt the random data
           with a length equal to that of the modulus of the RSA key.
         */
  
 -      if(RSA_public_encrypt(len, (unsigned char *)c->outkey, (unsigned char *)buffer, c->rsa_key, RSA_NO_PADDING) != len) {
 -              logger(LOG_ERR, "Error during encryption of meta key for %s (%s)",
 -                         c->name, c->hostname);
 +      if(!rsa_public_encrypt(&c->rsa, key, len, enckey)) {
 +              logger(LOG_ERR, "Error during encryption of meta key for %s (%s)", c->name, c->hostname);
                return false;
        }
  
        /* Convert the encrypted random data to a hexadecimal formatted string */
  
 -      bin2hex(buffer, buffer, len);
 -      buffer[len * 2] = '\0';
 +      bin2hex(enckey, hexkey, len);
 +      hexkey[len * 2] = '\0';
  
        /* Send the meta key */
  
 -      x = send_request(c, "%d %d %d %d %d %s", METAKEY,
 -                                       c->outcipher ? c->outcipher->nid : 0,
 -                                       c->outdigest ? c->outdigest->type : 0, c->outmaclength,
 -                                       c->outcompression, buffer);
 -
 -      /* Further outgoing requests are encrypted with the key we just generated */
 -
 -      if(c->outcipher) {
 -              if(!EVP_EncryptInit(c->outctx, c->outcipher,
 -                                      (unsigned char *)c->outkey + len - c->outcipher->key_len,
 -                                      (unsigned char *)c->outkey + len - c->outcipher->key_len -
 -                                      c->outcipher->iv_len)) {
 -                      logger(LOG_ERR, "Error during initialisation of cipher for %s (%s): %s",
 -                                      c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
 -                      return false;
 -              }
 -
 -              c->status.encryptout = true;
 -      }
 -
 -      return x;
 +      bool result = send_request(c, "%d %d %d %d %d %s", METAKEY,
 +                       cipher_get_nid(&c->outcipher),
 +                       digest_get_nid(&c->outdigest), c->outmaclength,
 +                       c->outcompression, hexkey);
 +      
 +      c->status.encryptout = true;
 +      return result;
  }
  
 -bool metakey_h(connection_t *c) {
 -      char buffer[MAX_STRING_SIZE];
 +bool metakey_h(connection_t *c, char *request) {
 +      char hexkey[MAX_STRING_SIZE];
        int cipher, digest, maclength, compression;
 -      int len;
 +      size_t len = rsa_size(&myself->connection->rsa);
 +      char enckey[len];
 +      char key[len];
  
 -      if(sscanf(c->buffer, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, buffer) != 5) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name,
 -                         c->hostname);
 +      if(sscanf(request, "%*d %d %d %d %d " MAX_STRING, &cipher, &digest, &maclength, &compression, hexkey) != 5) {
 +              logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
                return false;
        }
  
 -      len = RSA_size(myself->connection->rsa_key);
 -
        /* Check if the length of the meta key is all right */
  
 -      if(strlen(buffer) != len * 2) {
 +      if(strlen(hexkey) != len * 2) {
                logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong keylength");
                return false;
        }
  
 -      /* Allocate buffers for the meta key */
 -
 -      c->inkey = xrealloc(c->inkey, len);
 -
 -      if(!c->inctx)
 -              c->inctx = xmalloc_and_zero(sizeof(*c->inctx));
 -
        /* Convert the challenge from hexadecimal back to binary */
  
 -      hex2bin(buffer, buffer, len);
 +      hex2bin(hexkey, enckey, len);
  
        /* Decrypt the meta key */
  
 -      if(RSA_private_decrypt(len, (unsigned char *)buffer, (unsigned char *)c->inkey, myself->connection->rsa_key, RSA_NO_PADDING) != len) {  /* See challenge() */
 -              logger(LOG_ERR, "Error during decryption of meta key for %s (%s)",
 -                         c->name, c->hostname);
 +      if(!rsa_private_decrypt(&myself->connection->rsa, enckey, len, key)) {
 +              logger(LOG_ERR, "Error during decryption of meta key for %s (%s)", c->name, c->hostname);
                return false;
        }
  
        ifdebug(SCARY_THINGS) {
 -              bin2hex(c->inkey, buffer, len);
 -              buffer[len * 2] = '\0';
 -              logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", buffer);
 +              bin2hex(key, hexkey, len);
 +              hexkey[len * 2] = '\0';
 +              logger(LOG_DEBUG, "Received random meta key (unencrypted): %s", hexkey);
        }
  
 -      /* All incoming requests will now be encrypted. */
 -
        /* Check and lookup cipher and digest algorithms */
  
 -      if(cipher) {
 -              c->incipher = EVP_get_cipherbynid(cipher);
 -              
 -              if(!c->incipher) {
 -                      logger(LOG_ERR, "%s (%s) uses unknown cipher!", c->name, c->hostname);
 -                      return false;
 -              }
 -
 -              if(!EVP_DecryptInit(c->inctx, c->incipher,
 -                                      (unsigned char *)c->inkey + len - c->incipher->key_len,
 -                                      (unsigned char *)c->inkey + len - c->incipher->key_len -
 -                                      c->incipher->iv_len)) {
 -                      logger(LOG_ERR, "Error during initialisation of cipher from %s (%s): %s",
 -                                      c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
 -                      return false;
 -              }
 -
 -              c->status.decryptin = true;
 -      } else {
 -              c->incipher = NULL;
 +      if(!cipher_open_by_nid(&c->incipher, cipher) || !cipher_set_key_from_rsa(&c->incipher, key, len, false)) {
 +              logger(LOG_ERR, "Error during initialisation of cipher from %s (%s)", c->name, c->hostname);
 +              return false;
        }
  
 -      c->inmaclength = maclength;
 -
 -      if(digest) {
 -              c->indigest = EVP_get_digestbynid(digest);
 -
 -              if(!c->indigest) {
 -                      logger(LOG_ERR, "Node %s (%s) uses unknown digest!", c->name, c->hostname);
 -                      return false;
 -              }
 -
 -              if(c->inmaclength > c->indigest->md_size || c->inmaclength < 0) {
 -                      logger(LOG_ERR, "%s (%s) uses bogus MAC length!", c->name, c->hostname);
 -                      return false;
 -              }
 -      } else {
 -              c->indigest = NULL;
 +      if(!digest_open_by_nid(&c->indigest, digest, -1)) {
 +              logger(LOG_ERR, "Error during initialisation of digest from %s (%s)", c->name, c->hostname);
 +              return false;
        }
  
 -      c->incompression = compression;
 +      c->status.decryptin = true;
  
        c->allow_request = CHALLENGE;
  
  }
  
  bool send_challenge(connection_t *c) {
 -      char *buffer;
 -      int len;
 -
 -      /* CHECKME: what is most reasonable value for len? */
 -
 -      len = RSA_size(c->rsa_key);
 -
 -      /* Allocate buffers for the challenge */
 +      size_t len = rsa_size(&c->rsa);
 +      char buffer[len * 2 + 1];
  
 -      buffer = alloca(2 * len + 1);
 -
 -      c->hischallenge = xrealloc(c->hischallenge, len);
 +      if(!c->hischallenge)
 +              c->hischallenge = xrealloc(c->hischallenge, len);
  
        /* Copy random data to the buffer */
  
 -      RAND_pseudo_bytes((unsigned char *)c->hischallenge, len);
 +      randomize(c->hischallenge, len);
  
        /* Convert to hex */
  
        return send_request(c, "%d %s", CHALLENGE, buffer);
  }
  
 -bool challenge_h(connection_t *c) {
 +bool challenge_h(connection_t *c, char *request) {
        char buffer[MAX_STRING_SIZE];
 -      int len;
 +      size_t len = rsa_size(&myself->connection->rsa);
 +      size_t digestlen = digest_length(&c->indigest);
 +      char digest[digestlen];
  
 -      if(sscanf(c->buffer, "%*d " MAX_STRING, buffer) != 1) {
 -              logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name,
 -                         c->hostname);
 +      if(sscanf(request, "%*d " MAX_STRING, buffer) != 1) {
 +              logger(LOG_ERR, "Got bad %s from %s (%s)", "CHALLENGE", c->name, c->hostname);
                return false;
        }
  
 -      len = RSA_size(myself->connection->rsa_key);
 -
        /* Check if the length of the challenge is all right */
  
        if(strlen(buffer) != len * 2) {
 -              logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
 -                         c->hostname, "wrong challenge length");
 +              logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge length");
                return false;
        }
  
 -      /* Allocate buffers for the challenge */
 -
 -      c->mychallenge = xrealloc(c->mychallenge, len);
 -
        /* Convert the challenge from hexadecimal back to binary */
  
 -      hex2bin(buffer, c->mychallenge, len);
 +      hex2bin(buffer, buffer, len);
  
        c->allow_request = CHAL_REPLY;
  
 -      /* Rest is done by send_chal_reply() */
 -
 -      return send_chal_reply(c);
 -}
 -
 -bool send_chal_reply(connection_t *c) {
 -      char hash[EVP_MAX_MD_SIZE * 2 + 1];
 -      EVP_MD_CTX ctx;
 -
        /* Calculate the hash from the challenge we received */
  
 -      if(!EVP_DigestInit(&ctx, c->indigest)
 -                      || !EVP_DigestUpdate(&ctx, c->mychallenge, RSA_size(myself->connection->rsa_key))
 -                      || !EVP_DigestFinal(&ctx, (unsigned char *)hash, NULL)) {
 -              logger(LOG_ERR, "Error during calculation of response for %s (%s): %s",
 -                      c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
 -              return false;
 -      }
 +      digest_create(&c->indigest, buffer, len, digest);
  
        /* Convert the hash to a hexadecimal formatted string */
  
 -      bin2hex(hash, hash, c->indigest->md_size);
 -      hash[c->indigest->md_size * 2] = '\0';
 +      bin2hex(digest, buffer, digestlen);
 +      buffer[digestlen * 2] = '\0';
  
        /* Send the reply */
  
 -      return send_request(c, "%d %s", CHAL_REPLY, hash);
 +      return send_request(c, "%d %s", CHAL_REPLY, buffer);
  }
  
 -bool chal_reply_h(connection_t *c) {
 +bool chal_reply_h(connection_t *c, char *request) {
        char hishash[MAX_STRING_SIZE];
 -      char myhash[EVP_MAX_MD_SIZE];
 -      EVP_MD_CTX ctx;
  
 -      if(sscanf(c->buffer, "%*d " MAX_STRING, hishash) != 1) {
 +      if(sscanf(request, "%*d " MAX_STRING, hishash) != 1) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "CHAL_REPLY", c->name,
                           c->hostname);
                return false;
  
        /* Check if the length of the hash is all right */
  
 -      if(strlen(hishash) != c->outdigest->md_size * 2) {
 -              logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
 -                         c->hostname, "wrong challenge reply length");
 +      if(strlen(hishash) != digest_length(&c->outdigest) * 2) {
 +              logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply length");
                return false;
        }
  
        /* Convert the hash to binary format */
  
 -      hex2bin(hishash, hishash, c->outdigest->md_size);
 -
 -      /* Calculate the hash from the challenge we sent */
 +      hex2bin(hishash, hishash, digest_length(&c->outdigest));
  
 -      if(!EVP_DigestInit(&ctx, c->outdigest)
 -                      || !EVP_DigestUpdate(&ctx, c->hischallenge, RSA_size(c->rsa_key))
 -                      || !EVP_DigestFinal(&ctx, (unsigned char *)myhash, NULL)) {
 -              logger(LOG_ERR, "Error during calculation of response from %s (%s): %s",
 -                      c->name, c->hostname, ERR_error_string(ERR_get_error(), NULL));
 -              return false;
 -      }
 -
 -      /* Verify the incoming hash with the calculated hash */
 -
 -      if(memcmp(hishash, myhash, c->outdigest->md_size)) {
 -              logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name,
 -                         c->hostname, "wrong challenge reply");
 -
 -              ifdebug(SCARY_THINGS) {
 -                      bin2hex(myhash, hishash, SHA_DIGEST_LENGTH);
 -                      hishash[SHA_DIGEST_LENGTH * 2] = '\0';
 -                      logger(LOG_DEBUG, "Expected challenge reply: %s", hishash);
 -              }
 +      /* Verify the hash */
  
 +      if(!digest_verify(&c->outdigest, c->hischallenge, rsa_size(&c->rsa), hishash)) {
 +              logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
                return false;
        }
  
           Send an acknowledgement with the rest of the information needed.
         */
  
 +      free(c->hischallenge);
 +      c->hischallenge = NULL;
        c->allow_request = ACK;
  
        return send_ack(c);
@@@ -368,7 -464,7 +368,7 @@@ bool send_ack(connection_t *c) 
  }
  
  static void send_everything(connection_t *c) {
 -      avl_node_t *node, *node2;
 +      splay_node_t *node, *node2;
        node_t *n;
        subnet_t *s;
        edge_t *e;
        }
  }
  
 -bool ack_h(connection_t *c) {
 +bool ack_h(connection_t *c, char *request) {
        char hisport[MAX_STRING_SIZE];
        char *hisaddress;
        int weight, mtu;
        node_t *n;
        bool choice;
  
 -      if(sscanf(c->buffer, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
 +      if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &weight, &options) != 3) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ACK", c->name,
                           c->hostname);
                return false;
        } else {
                if(n->connection) {
                        /* Oh dear, we already have a connection to this node. */
 -                      ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection",
 -                                         n->name, n->hostname);
 +                      ifdebug(CONNECTIONS) logger(LOG_DEBUG, "Established a second connection with %s (%s), closing old connection", n->connection->name, n->connection->hostname);
 +
 +                      if(n->connection->outgoing) {
 +                              if(c->outgoing)
 +                                      logger(LOG_WARNING, "Two outgoing connections to the same node!");
 +                              else
 +                                      c->outgoing = n->connection->outgoing;
 +
 +                              n->connection->outgoing = NULL;
 +                      }
 +
                        terminate_connection(n->connection, false);
                        /* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */
                        graph();
        if(get_config_int(lookup_config(c->config_tree, "PMTU"), &mtu) && mtu < n->mtu)
                n->mtu = mtu;
  
-       if(get_config_int(lookup_config(myself->connection->config_tree, "PMTU"), &mtu) && mtu < n->mtu)
+       if(get_config_int(lookup_config(config_tree, "PMTU"), &mtu) && mtu < n->mtu)
                n->mtu = mtu;
  
        if(get_config_bool(lookup_config(c->config_tree, "ClampMSS"), &choice)) {
diff --combined src/protocol_edge.c
index 4aad53f012da36bd16852d945612c4ce6e1fa2f0,32102d27af27a04cb6381a7129a97a45fef057df..1dd68d5e86c71db1c2df12afdbde59e031d629ad
@@@ -21,7 -21,7 +21,7 @@@
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
  #include "conf.h"
  #include "connection.h"
  #include "edge.h"
@@@ -50,7 -50,7 +50,7 @@@ bool send_add_edge(connection_t *c, con
        return x;
  }
  
 -bool add_edge_h(connection_t *c) {
 +bool add_edge_h(connection_t *c, char *request) {
        edge_t *e;
        node_t *from, *to;
        char from_name[MAX_STRING_SIZE];
@@@ -61,7 -61,7 +61,7 @@@
        uint32_t options;
        int weight;
  
 -      if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
 +      if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING" "MAX_STRING" "MAX_STRING" %x %d",
                          from_name, to_name, to_address, to_port, &options, &weight) != 6) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name,
                           c->hostname);
@@@ -76,7 -76,7 +76,7 @@@
                return false;
        }
  
 -      if(seen_request(c->buffer))
 +      if(seen_request(request))
                return true;
  
        /* Lookup nodes */
        } else if(from == myself) {
                ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself which does not exist",
                                   "ADD_EDGE", c->name, c->hostname);
+               contradicting_add_edge++;
                e = new_edge();
                e->from = from;
                e->to = to;
        /* Tell the rest about the new edge */
  
        if(!tunnelserver)
 -              forward_request(c);
 +              forward_request(c, request);
  
        /* Run MST before or after we tell the rest? */
  
@@@ -166,13 -167,13 +167,13 @@@ bool send_del_edge(connection_t *c, con
                                                e->from->name, e->to->name);
  }
  
 -bool del_edge_h(connection_t *c) {
 +bool del_edge_h(connection_t *c, char *request) {
        edge_t *e;
        char from_name[MAX_STRING_SIZE];
        char to_name[MAX_STRING_SIZE];
        node_t *from, *to;
  
 -      if(sscanf(c->buffer, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
 +      if(sscanf(request, "%*d %*x "MAX_STRING" "MAX_STRING, from_name, to_name) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "DEL_EDGE", c->name,
                           c->hostname);
                return false;
                return false;
        }
  
 -      if(seen_request(c->buffer))
 +      if(seen_request(request))
                return true;
  
        /* Lookup nodes */
        if(e->from == myself) {
                ifdebug(PROTOCOL) logger(LOG_WARNING, "Got %s from %s (%s) for ourself",
                                   "DEL_EDGE", c->name, c->hostname);
+               contradicting_del_edge++;
                send_add_edge(c, e);    /* Send back a correction */
                return true;
        }
        /* Tell the rest about the deleted edge */
  
        if(!tunnelserver)
 -              forward_request(c);
 +              forward_request(c, request);
  
        /* Delete the edge */
  
diff --combined src/protocol_key.c
index 4187538b6e3f39f9be301a4ba9dc3042cb2ce836,22692bb614e97632d2224bedbd77b1fb28647976..f57dc2ea059ff07d01d104c8e3df46b48c60287a
  
  #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 "utils.h"
  #include "xalloc.h"
  
 -bool mykeyused = false;
 +static bool mykeyused = false;
  
  void send_key_changed() {
 -      avl_node_t *node;
 +      splay_node_t *node;
        connection_t *c;
  
        send_request(broadcast, "%d %x %s", KEY_CHANGED, rand(), myself->name);
        }
  }
  
 -bool key_changed_h(connection_t *c) {
 +bool key_changed_h(connection_t *c, char *request) {
        char name[MAX_STRING_SIZE];
        node_t *n;
  
 -      if(sscanf(c->buffer, "%*d %*x " MAX_STRING, name) != 1) {
 +      if(sscanf(request, "%*d %*x " MAX_STRING, name) != 1) {
                logger(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);
@@@ -76,7 -83,7 +76,7 @@@
        /* Tell the others */
  
        if(!tunnelserver)
 -              forward_request(c);
 +              forward_request(c, request);
  
        return true;
  }
@@@ -85,12 -92,12 +85,12 @@@ bool send_req_key(node_t *to) 
        return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
  }
  
 -bool req_key_h(connection_t *c) {
 +bool req_key_h(connection_t *c, char *request) {
        char from_name[MAX_STRING_SIZE];
        char to_name[MAX_STRING_SIZE];
        node_t *from, *to;
  
 -      if(sscanf(c->buffer, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
 +      if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING, from_name, to_name) != 2) {
                logger(LOG_ERR, "Got bad %s from %s (%s)", "REQ_KEY", c->name,
                           c->hostname);
                return false;
        /* Check if this key request is for us */
  
        if(to == myself) {                      /* Yes, send our own key back */
 +
                send_ans_key(from);
        } else {
                if(tunnelserver)
                        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) {
 -      char *key;
 +      size_t keylen = cipher_keylength(&myself->incipher);
 +      char key[keylen * 2 + 1];
  
 -      // Set key parameters
 -      to->incipher = myself->incipher;
 -      to->inkeylength = myself->inkeylength;
 -      to->indigest = myself->indigest;
 -      to->inmaclength = myself->inmaclength;
 +      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, true);
 +      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);
 +      key[keylen * 2] = '\0';
  
        // Reset sequence number and late packet window
        mykeyused = true;
        to->received_seqno = 0;
        memset(to->late, 0, sizeof(to->late));
  
 -      // Convert to hexadecimal and send
 -      key = alloca(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);
 +      return send_request(to->nexthop->connection, "%d %s %s %s %d %d %zu %d", ANS_KEY,
 +                                              myself->name, to->name, key,
 +                                              cipher_get_nid(&to->incipher),
 +                                              digest_get_nid(&to->indigest),
 +                                              digest_length(&to->indigest),
 +                                              to->incompression);
  }
  
 -bool ans_key_h(connection_t *c) {
 +bool ans_key_h(connection_t *c, 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,
  
                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);
 +                                 "ANS_KEY", c->name, c->hostname, to_name);
                        return true;
                }
  
-               if(!*address) {
+               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);
                        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->outkey = xstrdup(key);
 -      from->outkeylength = strlen(key) / 2;
 -      hex2bin(key, from->outkey, from->outkeylength);
 -
        /* Check and lookup cipher and digest algorithms */
  
 -      if(cipher) {
 -              from->outcipher = EVP_get_cipherbynid(cipher);
 -
 -              if(!from->outcipher) {
 -                      logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name,
 -                                 from->hostname);
 -                      return true;
 -              }
 -
 -              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;
 -              }
 -      } else {
 -              from->outcipher = NULL;
 +      if(!cipher_open_by_nid(&from->outcipher, cipher)) {
 +              logger(LOG_ERR, "Node %s (%s) uses unknown cipher!", from->name, from->hostname);
 +              return false;
        }
  
 -      from->outmaclength = maclength;
 +      keylen = strlen(key) / 2;
  
 -      if(digest) {
 -              from->outdigest = EVP_get_digestbynid(digest);
 +      if(keylen != cipher_keylength(&from->outcipher)) {
 +              logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", 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(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(LOG_ERR, "Node %s (%s) uses bogus MAC length!", from->name, from->hostname);
 +              return false;
        }
  
        if(compression < 0 || compression > 11) {
        
        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 */
 +
 +      hex2bin(key, key, keylen);
 +      cipher_set_key(&from->outcipher, key, false);
 +      digest_set_key(&from->outdigest, key, keylen);
  
        from->status.validkey = true;
        from->sent_seqno = 0;
diff --combined src/raw_socket/device.c
index 0ee7cc20e5bbb95ef1ab4870937e455b22b56e53,f68e461973f6159085d19f5e917d9566e05e7a78..66d63487e7235365098bcf62eba6e6b4967c4136
@@@ -35,8 -35,8 +35,8 @@@ char *iface = NULL
  static char ifrname[IFNAMSIZ];
  static char *device_info;
  
- static int device_total_in = 0;
- static int device_total_out = 0;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
  
  bool setup_device(void) {
        struct ifreq ifr;
@@@ -56,7 -56,7 +56,7 @@@
                return false;
        }
  
 -      memset(&ifr, 0, sizeof(ifr));
 +      memset(&ifr, 0, sizeof ifr);
        strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
        if(ioctl(device_fd, SIOCGIFINDEX, &ifr)) {
                close(device_fd);
                return false;
        }
  
 -      memset(&sa, '0', sizeof(sa));
 +      memset(&sa, '0', sizeof sa);
        sa.sll_family = AF_PACKET;
        sa.sll_protocol = htons(ETH_P_ALL);
        sa.sll_ifindex = ifr.ifr_ifindex;
  
 -      if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof(sa))) {
 +      if(bind(device_fd, (struct sockaddr *) &sa, (socklen_t) sizeof sa)) {
                logger(LOG_ERR, "Could not bind %s to %s: %s", device, iface, strerror(errno));
                return false;
        }
@@@ -88,15 -88,15 +88,15 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
 -      if((lenin = read(device_fd, packet->data, MTU)) <= 0) {
 +      if((inlen = read(device_fd, packet->data, MTU)) <= 0) {
                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
        }
  
 -      packet->len = lenin;
 +      packet->len = inlen;
  
        device_total_in += packet->len;
  
@@@ -123,6 -123,6 +123,6 @@@ bool write_packet(vpn_packet_t *packet
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10d", device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10d", device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  }
diff --combined src/solaris/device.c
index 8221b9fd02fadd491f029d4f183964befff0a7da,9018c31a0811011ee661aafb88b32846019e3a08..d257ffdde2be1e621fa85150e62fddadc681ae0e
@@@ -38,8 -38,8 +38,8 @@@ char *device = NULL
  char *iface = NULL;
  static char *device_info = NULL;
  
- static int device_total_in = 0;
- static int device_total_out = 0;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
  
  bool setup_device(void) {
        int ip_fd = -1, if_fd = -1;
@@@ -112,9 -112,9 +112,9 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
 -      if((lenin = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
 +      if((inlen = read(device_fd, packet->data + 14, MTU - 14)) <= 0) {
                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                           device, strerror(errno));
                return false;
                        return false;
        }
  
 -      packet->len = lenin + 14;
 +      packet->len = inlen + 14;
  
        device_total_in += packet->len;
  
@@@ -163,6 -163,6 +163,6 @@@ bool write_packet(vpn_packet_t *packet
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10d", device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10d", device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  }
diff --combined src/tincd.c
index 21623647fb1eb0be7876a74c49fa16d19263494b,3dab9a01157b5751c14a65f24fc3d82ae94ea94f..c4750da20c0c8b6991285a4ceeb25cea4778dd29
@@@ -4,6 -4,8 +4,8 @@@
                    2000-2010 Guus Sliepen <guus@tinc-vpn.org>
                    2008      Max Rijevski <maksuf@gmail.com>
                    2009      Michael Tokarev <mjt@tls.msk.ru>
+                   2010      Julien Muchembled <jm@jmuchemb.eu>
+                   2010      Timothy Redaelli <timothy@redaelli.eu>
  
      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
  #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"
@@@ -70,6 -71,12 +72,6 @@@ 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;
 -
  /* If nonzero, use null ciphers and skip all key exchanges. */
  bool bypass_security = false;
  
@@@ -86,31 -93,34 +88,32 @@@ 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 *controlcookiename = NULL;
  char **g_argv;                                        /* a copy of the cmdline arguments */
  
  static int status;
  
  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'},
        {"chroot", no_argument, NULL, 'R'},
        {"user", required_argument, NULL, 'U'},
        {"logfile", optional_argument, NULL, 4},
 -      {"pidfile", required_argument, NULL, 5},
 +      {"controlcookie", required_argument, NULL, 5},
        {NULL, 0, NULL, 0}
  };
  
  #ifdef HAVE_MINGW
  static struct WSAData wsa_state;
  CRITICAL_SECTION mutex;
+ int main2(int argc, char **argv);
  #endif
  
  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 [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"
 +                              "      --controlcookie=FILENAME  Write control socket cookie to FILENAME.\n"
 +                              "      --bypass-security         Disables meta protocol security, for debugging.\n"
++                              "  -o [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");
        }
  }
  
  static bool parse_options(int argc, char **argv) {
+       config_t *cfg;
        int r;
        int option_index = 0;
+       int lineno = 0;
  
-       while((r = getopt_long(argc, argv, "c:DLd::n:RU:", long_options, &option_index)) != EOF) {
+       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;
                                        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 {
 -                                              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 = xstrdup(optarg);
+                               /* netname "." is special: a "top-level name" */
+                               netname = strcmp(optarg, ".") != 0 ?
+                                               xstrdup(optarg) : NULL;
+                               break;
+                       case 'o':                               /* option */
+                               cfg = parse_config_line(optarg, NULL, ++lineno);
+                               if (!cfg)
+                                       return false;
+                               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 */
 -                              pidfilename = xstrdup(optarg);
 +                      case 5:                                 /* open control socket here */
 +                              controlcookiename = xstrdup(optarg);
                                break;
  
                        case '?':
        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;
 -
 -      if(disable_old_keys(f))
 -              fprintf(stderr, "Warning: old key(s) found and disabled.\n");
 -  
 -#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;
 -
 -      if(disable_old_keys(f))
 -              fprintf(stderr, "Warning: old key(s) found and disabled.\n");
 -
 -      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
  */
@@@ -220,7 -402,7 +237,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(!controlcookiename)
 +                              xasprintf(&controlcookiename, "%s/cookie", 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(!controlcookiename)
 +              xasprintf(&controlcookiename, LOCALSTATEDIR "/run/%s.cookie", identname);
 +
        if(netname) {
                if(!confbase)
                        xasprintf(&confbase, CONFDIR "/tinc/%s", netname);
  static void free_names() {
        if (identname) free(identname);
        if (netname) free(netname);
 -      if (pidfilename) free(pidfilename);
 +      if (controlcookiename) free(controlcookiename);
        if (logfilename) free(logfilename);
        if (confbase) free(confbase);
  }
@@@ -355,28 -535,28 +372,28 @@@ int main(int argc, char **argv) 
                return 0;
        }
  
 -      if(kill_tincd)
 -              return !kill_other(kill_tincd);
 +#ifdef HAVE_MINGW
 +      if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
 +              logger(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(LOG_ERR, "Error initializing libevent!");
 +              return 1;
 +      }
 +
        g_argv = argv;
  
        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;
  #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
@@@ -419,9 -604,6 +436,9 @@@ int main2(int argc, char **argv) 
        if(!setup_network())
                goto end;
  
 +      if(!init_control())
 +              return 1;
 +
        /* Initiate all outgoing connections. */
  
        try_outgoing_connections();
          char *priority = 0;
  
          if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
-                 if(!strcasecmp(priority, "Normal"))
-                         setpriority(NORMAL_PRIORITY_CLASS);
-                 else if(!strcasecmp(priority, "Low"))
-                         setpriority(BELOW_NORMAL_PRIORITY_CLASS);
-                 else if(!strcasecmp(priority, "High"))
-                         setpriority(HIGH_PRIORITY_CLASS);
-                 else {
+                 if(!strcasecmp(priority, "Normal")) {
+                         if (setpriority(NORMAL_PRIORITY_CLASS) != 0) {
+                                 logger(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",
+                                        "setpriority", strerror(errno));
+                                 goto end;
+                         }
+                 } else if(!strcasecmp(priority, "High")) {
+                         if (setpriority(HIGH_PRIORITY_CLASS) != 0) {
+                                 logger(LOG_ERR, "System call `%s' failed: %s",
+                                        "setpriority", strerror(errno));
+                                 goto end;
+                         }
+                 } else {
                          logger(LOG_ERR, "Invalid priority `%s`!", priority);
                          goto end;
                  }
  end:
        logger(LOG_NOTICE, "Terminating");
  
 -#ifndef HAVE_MINGW
 -      remove_pid(pidfilename);
 -#endif
 +      exit_control();
  
 -      EVP_cleanup();
 -      ENGINE_cleanup();
 -      CRYPTO_cleanup_all_ex_data();
 -      ERR_remove_state(0);
 -      ERR_free_strings();
 +      crypto_exit();
  
        exit_configuration(&config_tree);
        free_names();
diff --combined src/uml_socket/device.c
index ddb4563bfb413e61b5252cdf464a7d20d1aa1d67,59551b4f51b1d94a4d15c69d24dbb6babc8710cf..e9b07663acaf4fd2f9140228a35342b7e6a8d9e9
@@@ -41,8 -41,8 +41,8 @@@ static char *device_info
  extern char *identname;
  extern bool running;
  
- static int device_total_in = 0;
- static int device_total_out = 0;
+ static uint64_t device_total_in = 0;
+ static uint64_t device_total_out = 0;
  
  enum request_type { REQ_NEW_CONTROL };
  
@@@ -169,7 -169,7 +169,7 @@@ void close_device(void) 
  }
  
  bool read_packet(vpn_packet_t *packet) {
 -      int lenin;
 +      int inlen;
  
        switch(state) {
                case 0: {
                }
  
                case 1: {
 -                      if((lenin = read(request_fd, &request, sizeof request)) != sizeof request) {
 +                      if((inlen = read(request_fd, &request, sizeof request)) != sizeof request) {
                                logger(LOG_ERR, "Error while reading request from %s %s: %s", device_info,
                                           device, strerror(errno));
                                running = false;
                }
  
                case 2: {
 -                      if((lenin = read(data_fd, packet->data, MTU)) <= 0) {
 +                      if((inlen = read(data_fd, packet->data, MTU)) <= 0) {
                                logger(LOG_ERR, "Error while reading from %s %s: %s", device_info,
                                           device, strerror(errno));
                                running = false;
                                return false;
                        }
  
 -                      packet->len = lenin;
 +                      packet->len = inlen;
  
                        device_total_in += packet->len;
  
@@@ -272,6 -272,6 +272,6 @@@ bool write_packet(vpn_packet_t *packet
  
  void dump_device_stats(void) {
        logger(LOG_DEBUG, "Statistics for %s %s:", device_info, device);
-       logger(LOG_DEBUG, " total bytes in:  %10d", device_total_in);
-       logger(LOG_DEBUG, " total bytes out: %10d", device_total_out);
+       logger(LOG_DEBUG, " total bytes in:  %10"PRIu64, device_total_in);
+       logger(LOG_DEBUG, " total bytes out: %10"PRIu64, device_total_out);
  }