Merge branch 'master' into 1.1
authorGuus Sliepen <guus@tinc-vpn.org>
Fri, 19 Nov 2010 12:22:48 +0000 (12:22 +0000)
committerGuus Sliepen <guus@tinc-vpn.org>
Fri, 19 Nov 2010 12:22:48 +0000 (12:22 +0000)
Conflicts:
src/net_packet.c
src/openssl/rsagen.h
src/protocol_auth.c
src/protocol_key.c

1  2 
src/dropin.c
src/linux/device.c
src/net.h
src/net_packet.c
src/net_setup.c
src/net_socket.c
src/node.c
src/node.h
src/process.c
src/protocol_key.c

diff --combined src/dropin.c
@@@ -163,3 -163,10 +163,10 @@@ int gettimeofday(struct timeval *tv, vo
        return 0;
  }
  #endif
+ #ifdef HAVE_MINGW
+ int usleep(long usec) {
+       Sleep(usec / 1000);
+       return 0;
+ }
+ #endif
diff --combined src/linux/device.c
@@@ -52,6 -52,7 +52,7 @@@ static uint64_t device_total_out = 0
  
  bool setup_device(void) {
        struct ifreq ifr;
+       bool t1q = false;
  
        if(!get_config_string(lookup_config(config_tree, "Device"), &device))
                device = xstrdup(DEFAULT_DEVICE);
@@@ -73,7 -74,7 +74,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_info = "Linux tun/tap device (tap mode)";
        }
  
+ #ifdef IFF_ONE_QUEUE
+       /* Set IFF_ONE_QUEUE flag... */
+       if(get_config_bool(lookup_config(config_tree, "IffOneQueue"), &t1q) && t1q)
+               ifr.ifr_flags |= IFF_ONE_QUEUE;
+ #endif
        if(iface)
                strncpy(ifr.ifr_name, iface, IFNAMSIZ);
  
@@@ -121,41 -128,41 +128,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;
        }
  
diff --combined src/net.h
+++ 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;
  extern int maxoutbufsize;
  extern int seconds_till_retry;
  extern int addressfamily;
+ extern unsigned replaywin;
  
  extern listen_socket_t listen_socket[MAXSOCKETS];
  extern int listen_sockets;
 -extern int keyexpires;
  extern int keylifetime;
+ extern int udp_rcvbuf;
+ extern int udp_sndbuf;
  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;
  
  #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 *);
@@@ -140,12 -144,6 +143,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
@@@ -3,6 -3,7 +3,7 @@@
      Copyright (C) 1998-2005 Ivo Timmermans,
                    2000-2010 Guus Sliepen <guus@tinc-vpn.org>
                    2010      Timothy Redaelli <timothy@redaelli.eu>
+                   2010      Brandon Black <blblack@gmail.com>
  
      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"
@@@ -62,6 -61,8 +63,8 @@@ static char lzo_wrkmem[LZO1X_999_MEM_CO
  
  static void send_udppacket(node_t *, vpn_packet_t *);
  
+ unsigned replaywin = 16;
  #define MAX_SEQNO 1073741824
  
  // mtuprobes == 1..30: initial discovery, send bursts with 1 second interval
  // 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) {
@@@ -226,11 -225,15 +229,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) {
-                       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);
-               } 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))) {
-                               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;
+       if(replaywin) {
+               if(inpkt->seqno != n->received_seqno + 1) {
+                       if(inpkt->seqno >= n->received_seqno + replaywin * 8) {
+                               if(n->farfuture++ < replaywin >> 2) {
+                                       logger(LOG_WARNING, "Packet from %s (%s) is %d seqs in the future, dropped (%u)",
+                                               n->name, n->hostname, inpkt->seqno - n->received_seqno - 1, n->farfuture);
+                                       return;
+                               }
+                               logger(LOG_WARNING, "Lost %d packets from %s (%s)",
+                                               inpkt->seqno - n->received_seqno - 1, n->name, n->hostname);
+                               memset(n->late, 0, replaywin);
+                       } else if (inpkt->seqno <= n->received_seqno) {
+                               if((n->received_seqno >= replaywin * 8 && inpkt->seqno <= n->received_seqno - replaywin * 8) || !(n->late[(inpkt->seqno / 8) % replaywin] & (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) % replaywin] |= 1 << i % 8;
                        }
-               } else {
-                       for(i = n->received_seqno + 1; i < inpkt->seqno; i++)
-                               n->late[(i / 8) % sizeof n->late] |= 1 << i % 8;
                }
+               n->farfuture = 0;
+               n->late[(inpkt->seqno / 8) % replaywin] &= ~(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 */
  
@@@ -356,7 -375,7 +366,7 @@@ 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
        /* 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
@@@ -517,7 -538,7 +527,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
@@@ -3,6 -3,7 +3,7 @@@
      Copyright (C) 1998-2005 Ivo Timmermans,
                    2000-2010 Guus Sliepen <guus@tinc-vpn.org>
                    2006      Scott Lamb <slamb@slamb.org>
+                   2010      Brandon Black <blblack@gmail.com>
  
      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 "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(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 -210,7 +163,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;
@@@ -229,6 -277,7 +230,7 @@@ bool setup_myself(void) 
        struct addrinfo *ai, *aip, hint = {0};
        bool choice;
        int i, err;
+       int replaywin_int;
  
        myself = new_node();
        myself->connection = new_connection();
        } else
                maxtimeout = 900;
  
+       if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
+               if(udp_rcvbuf <= 0) {
+                       logger(LOG_ERR, "UDPRcvBuf cannot be negative!");
+                       return false;
+               }
+       }
+       if(get_config_int(lookup_config(config_tree, "UDPSndBuf"), &udp_sndbuf)) {
+               if(udp_sndbuf <= 0) {
+                       logger(LOG_ERR, "UDPSndBuf cannot be negative!");
+                       return false;
+               }
+       }
+       if(get_config_int(lookup_config(config_tree, "ReplayWindow"), &replaywin_int)) {
+               if(replaywin_int < 0) {
+                       logger(LOG_ERR, "ReplayWindow cannot be negative!");
+                       return false;
+               }
+               replaywin = (unsigned)replaywin_int;
+       }
        if(get_config_string(lookup_config(config_tree, "AddressFamily"), &afname)) {
                if(!strcasecmp(afname, "IPv4"))
                        addressfamily = AF_INET;
  
        /* Generate packet encryption key */
  
 -      if(get_config_string
 -         (lookup_config(config_tree, "Cipher"), &cipher)) {
 -              if(!strcasecmp(cipher, "none")) {
 -                      myself->incipher = NULL;
 -              } else {
 -                      myself->incipher = EVP_get_cipherbyname(cipher);
 +      if(!get_config_string(lookup_config(config_tree, "Cipher"), &cipher))
 +              cipher = xstrdup("blowfish");
  
 -                      if(!myself->incipher) {
 -                              logger(LOG_ERR, "Unrecognized cipher type!");
 -                              return false;
 -                      }
 -              }
 -      } else
 -              myself->incipher = EVP_bf_cbc();
 -
 -      if(myself->incipher)
 -              myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
 -      else
 -              myself->inkeylength = 1;
 -
 -      myself->connection->outcipher = EVP_bf_ofb();
 +      if(!cipher_open_by_name(&myself->incipher, cipher)) {
 +              logger(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(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(!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
  
  #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"
@@@ -43,6 -44,8 +43,8 @@@
  int addressfamily = AF_UNSPEC;
  int maxtimeout = 900;
  int seconds_till_retry = 5;
+ int udp_rcvbuf = 0;
+ int udp_sndbuf = 0;
  
  listen_socket_t listen_socket[MAXSOCKETS];
  int listen_sockets;
@@@ -69,12 -72,12 +71,12 @@@ static void configure_tcp(connection_t 
  
  #if defined(SOL_TCP) && defined(TCP_NODELAY)
        option = 1;
 -      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, (void *)&option, sizeof(option));
 +      setsockopt(c->socket, SOL_IP, IP_TOS, (void *)&option, sizeof option);
  #endif
  }
  
@@@ -180,7 -183,7 +182,7 @@@ int setup_listen_socket(const sockaddr_
        /* Optimize TCP settings */
  
        option = 1;
 -      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)
  #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, (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,8 -261,14 +260,14 @@@ int setup_vpn_in_socket(const sockaddr_
  #endif
  
        option = 1;
 -      setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));
 +      setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof option);
  
+       if(udp_rcvbuf && setsockopt(nfd, SOL_SOCKET, SO_RCVBUF, (void *)&udp_rcvbuf, sizeof(udp_rcvbuf)))
+               logger(LOG_WARNING, "Can't set UDP SO_RCVBUF to %i: %s", udp_rcvbuf, strerror(errno));
+       if(udp_sndbuf && setsockopt(nfd, SOL_SOCKET, SO_SNDBUF, (void *)&udp_sndbuf, sizeof(udp_sndbuf)))
+               logger(LOG_WARNING, "Can't set UDP SO_SNDBUF to %i: %s", udp_sndbuf, strerror(errno));
  #if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
        if(sa->sa.sa_family == AF_INET6)
                setsockopt(nfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&option, sizeof option);
        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 -346,7 +344,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 -365,9 +364,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;
                }
  
        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 -563,7 +593,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/node.c
@@@ -20,8 -20,7 +20,8 @@@
  
  #include "system.h"
  
 -#include "avl_tree.h"
 +#include "control_common.h"
 +#include "splay_tree.h"
  #include "logger.h"
  #include "net.h"
  #include "netutl.h"
@@@ -29,8 -28,8 +29,8 @@@
  #include "utils.h"
  #include "xalloc.h"
  
 -avl_tree_t *node_tree;                        /* Known nodes, sorted by name */
 -avl_tree_t *node_udp_tree;            /* Known nodes, sorted by address and port */
 +splay_tree_t *node_tree;                      /* Known nodes, sorted by name */
 +splay_tree_t *node_udp_tree;          /* Known nodes, sorted by address and port */
  
  node_t *myself;
  
@@@ -39,31 -38,27 +39,32 @@@ static int node_compare(const node_t *a
  }
  
  static int node_udp_compare(const node_t *a, const node_t *b) {
 -       return sockaddrcmp(&a->address, &b->address);
 +      int result;
 +
 +      result = sockaddrcmp(&a->address, &b->address);
 +
 +      if(result)
 +              return result;
 +
 +      return (a->name && b->name) ? strcmp(a->name, b->name) : 0;
  }
  
  void init_nodes(void) {
 -      node_tree = avl_alloc_tree((avl_compare_t) node_compare, (avl_action_t) free_node);
 -      node_udp_tree = avl_alloc_tree((avl_compare_t) node_udp_compare, NULL);
 +      node_tree = splay_alloc_tree((splay_compare_t) node_compare, (splay_action_t) free_node);
 +      node_udp_tree = splay_alloc_tree((splay_compare_t) node_udp_compare, NULL);
  }
  
  void exit_nodes(void) {
 -      avl_delete_tree(node_udp_tree);
 -      avl_delete_tree(node_tree);
 +      splay_delete_tree(node_udp_tree);
 +      splay_delete_tree(node_tree);
  }
  
  node_t *new_node(void) {
 -      node_t *n = xmalloc_and_zero(sizeof(*n));
 +      node_t *n = xmalloc_and_zero(sizeof *n);
  
+       if(replaywin) n->late = xmalloc_and_zero(replaywin);
        n->subnet_tree = new_subnet_tree();
        n->edge_tree = new_edge_tree();
 -      EVP_CIPHER_CTX_init(&n->inctx);
 -      EVP_CIPHER_CTX_init(&n->outctx);
        n->mtu = MTU;
        n->maxmtu = MTU;
  
  }
  
  void free_node(node_t *n) {
 -      if(n->inkey)
 -              free(n->inkey);
 -
 -      if(n->outkey)
 -              free(n->outkey);
 -
        if(n->subnet_tree)
                free_subnet_tree(n->subnet_tree);
  
  
        sockaddrfree(&n->address);
  
 -      EVP_CIPHER_CTX_cleanup(&n->inctx);
 -      EVP_CIPHER_CTX_cleanup(&n->outctx);
 +      cipher_close(&n->incipher);
 +      digest_close(&n->indigest);
 +      cipher_close(&n->outcipher);
 +      digest_close(&n->outdigest);
  
 -      if(n->mtuevent)
 -              event_del(n->mtuevent);
 +      event_del(&n->mtuevent);
        
        if(n->hostname)
                free(n->hostname);
        if(n->name)
                free(n->name);
  
+       if(n->late)
+               free(n->late);
        free(n);
  }
  
  void node_add(node_t *n) {
 -      avl_insert(node_tree, n);
 +      splay_insert(node_tree, n);
  }
  
  void node_del(node_t *n) {
 -      avl_node_t *node, *next;
 +      splay_node_t *node, *next;
        edge_t *e;
        subnet_t *s;
  
                edge_del(e);
        }
  
 -      avl_delete(node_udp_tree, n);
 -      avl_delete(node_tree, n);
 +      splay_delete(node_udp_tree, n);
 +      splay_delete(node_tree, n);
  }
  
  node_t *lookup_node(char *name) {
  
        n.name = name;
  
 -      return avl_search(node_tree, &n);
 +      return splay_search(node_tree, &n);
  }
  
  node_t *lookup_node_udp(const sockaddr_t *sa) {
        n.address = *sa;
        n.name = NULL;
  
 -      return avl_search(node_udp_tree, &n);
 +      return splay_search(node_udp_tree, &n);
  }
  
  void update_node_udp(node_t *n, const sockaddr_t *sa) {
 -      avl_delete(node_udp_tree, n);
 +      splay_delete(node_udp_tree, n);
  
        if(n->hostname)
                free(n->hostname);
        if(sa) {
                n->address = *sa;
                n->hostname = sockaddr2hostname(&n->address);
 -              avl_insert(node_udp_tree, n);
 -              ifdebug(PROTOCOL) logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
 +              splay_insert(node_udp_tree, n);
 +              logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
        } else {
                memset(&n->address, 0, sizeof n->address);
                n->hostname = 0;
        }
  }
  
 -void dump_nodes(void) {
 -      avl_node_t *node;
 +bool dump_nodes(connection_t *c) {
 +      splay_node_t *node;
        node_t *n;
  
 -      logger(LOG_DEBUG, "Nodes:");
 -
        for(node = node_tree->head; node; node = node->next) {
                n = node->data;
 -              logger(LOG_DEBUG, " %s at %s cipher %d digest %d maclength %d compression %d options %x status %04x nexthop %s via %s pmtu %d (min %d max %d)",
 -                         n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0,
 -                         n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression,
 +              send_request(c, "%d %d %s at %s cipher %d digest %d maclength %zd compression %d options %x status %04x nexthop %s via %s distance %d pmtu %d (min %d max %d)", CONTROL, REQ_DUMP_NODES,
 +                         n->name, n->hostname, cipher_get_nid(&n->outcipher),
 +                         digest_get_nid(&n->outdigest), digest_length(&n->outdigest), n->outcompression,
                           n->options, bitfield_to_int(&n->status, sizeof n->status), n->nexthop ? n->nexthop->name : "-",
 -                         n->via ? n->via->name : "-", n->mtu, n->minmtu, n->maxmtu);
 +                         n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu);
        }
  
 -      logger(LOG_DEBUG, "End of nodes.");
 +      return send_request(c, "%d %d", CONTROL, REQ_DUMP_NODES);
  }
diff --combined src/node.h
  #ifndef __TINC_NODE_H__
  #define __TINC_NODE_H__
  
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "connection.h"
 -#include "event.h"
 +#include "digest.h"
  #include "list.h"
  #include "subnet.h"
  
@@@ -48,39 -47,49 +48,40 @@@ typedef struct node_t 
        node_status_t status;
        time_t last_req_key;
  
 -      const EVP_CIPHER *incipher;             /* Cipher type for UDP packets received from him */
 -      char *inkey;                            /* Cipher key and iv */
 -      int inkeylength;                        /* Cipher key and iv length */
 -      EVP_CIPHER_CTX inctx;                   /* Cipher context */
 -      
 -      const EVP_CIPHER *outcipher;            /* Cipher type for UDP packets sent to him*/
 -      char *outkey;                           /* Cipher key and iv */
 -      int outkeylength;                       /* Cipher key and iv length */
 -      EVP_CIPHER_CTX outctx;                  /* Cipher context */
 -      
 -      const EVP_MD *indigest;                 /* Digest type for MAC of packets received from him */
 -      int inmaclength;                        /* Length of MAC */
 -
 -      const EVP_MD *outdigest;                /* Digest type for MAC of packets sent to him*/
 -      int outmaclength;                       /* Length of MAC */
 +      cipher_t incipher;                        /* Cipher for UDP packets */
 +      digest_t indigest;                        /* Digest for UDP packets */  
 +
 +      cipher_t outcipher;                        /* Cipher for UDP packets */
 +      digest_t outdigest;                        /* Digest for UDP packets */ 
  
        int incompression;                      /* Compressionlevel, 0 = no compression */
        int outcompression;                     /* Compressionlevel, 0 = no compression */
  
 +      int distance;
        struct node_t *nexthop;                 /* nearest node from us to him */
        struct node_t *via;                     /* next hop for UDP packets */
  
 -      avl_tree_t *subnet_tree;                /* Pointer to a tree of subnets belonging to this node */
 +      splay_tree_t *subnet_tree;              /* Pointer to a tree of subnets belonging to this node */
  
 -      avl_tree_t *edge_tree;                  /* Edges with this node as one of the endpoints */
 +      splay_tree_t *edge_tree;                        /* Edges with this node as one of the endpoints */
  
        struct connection_t *connection;        /* Connection associated with this node (if a direct connection exists) */
  
        uint32_t sent_seqno;                    /* Sequence number last sent to this node */
        uint32_t received_seqno;                /* Sequence number last received from this node */
-       unsigned char late[16];                 /* Bitfield marking late packets */
+       uint32_t farfuture;                     /* Packets in a row that have arrived from the far future */
+       unsigned char* late;                    /* Bitfield marking late packets */
  
        length_t mtu;                           /* Maximum size of packets to send to this node */
        length_t minmtu;                        /* Probed minimum MTU */
        length_t maxmtu;                        /* Probed maximum MTU */
        int mtuprobes;                          /* Number of probes */
 -      event_t *mtuevent;                      /* Probe event */
 +      struct event mtuevent;                  /* Probe event */
  } node_t;
  
  extern struct node_t *myself;
 -extern avl_tree_t *node_tree;
 -extern avl_tree_t *node_udp_tree;
 +extern splay_tree_t *node_tree;
 +extern splay_tree_t *node_udp_tree;
  
  extern void init_nodes(void);
  extern void exit_nodes(void);
@@@ -90,7 -99,7 +91,7 @@@ extern void node_add(node_t *)
  extern void node_del(node_t *);
  extern node_t *lookup_node(char *);
  extern node_t *lookup_node_udp(const sockaddr_t *);
 +extern bool dump_nodes(struct connection_t *);
  extern void update_node_udp(node_t *, const sockaddr_t *);
 -extern void dump_nodes(void);
  
  #endif                                                        /* __TINC_NODE_H__ */
diff --combined src/process.c
  
  #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);
@@@ -166,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) {
@@@ -227,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());
  bool execute_script(const char *name, char **envp) {
  #ifdef HAVE_SYSTEM
        int status, len;
-       char *scriptname, *p;
+       char *scriptname;
        int i;
  
  #ifndef HAVE_MINGW
        for(i = 0; envp[i]; i++) {
                char *e = strchr(envp[i], '=');
                if(e) {
-                       p = alloca(e - envp[i] + 1);
+                       char p[e - envp[i] + 1];
                        strncpy(p, envp[i], e - envp[i]);
                        p[e - envp[i]] = '\0';
                        putenv(p);
  */
  
  #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));
@@@ -362,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));
  }
@@@ -382,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
@@@ -413,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/protocol_key.c
  
  #include "system.h"
  
 -#include <openssl/evp.h>
 -#include <openssl/err.h>
 -#include <openssl/rand.h>
 -
 -#include "avl_tree.h"
 +#include "splay_tree.h"
 +#include "cipher.h"
  #include "connection.h"
 +#include "crypto.h"
  #include "logger.h"
  #include "net.h"
  #include "netutl.h"
  #include "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) {
 -      // Set key parameters
 -      to->incipher = myself->incipher;
 -      to->inkeylength = myself->inkeylength;
 -      to->indigest = myself->indigest;
 -      to->inmaclength = myself->inmaclength;
 +      size_t keylen = cipher_keylength(&myself->incipher);
 +      char key[keylen * 2 + 1];
 +
 +      cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
 +      digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
        to->incompression = myself->incompression;
  
 -      // Allocate memory for key
 -      to->inkey = xrealloc(to->inkey, to->inkeylength);
 +      randomize(key, keylen);
 +      cipher_set_key(&to->incipher, key, 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));
+       if(replaywin) memset(to->late, 0, replaywin);
  
 -      // Convert to hexadecimal and send
 -      char key[2 * to->inkeylength + 1];
 -      bin2hex(to->inkey, key, to->inkeylength);
 -      key[to->inkeylength * 2] = '\0';
 -
 -      return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
 -                      myself->name, to->name, key,
 -                      to->incipher ? to->incipher->nid : 0,
 -                      to->indigest ? to->indigest->type : 0, to->inmaclength,
 -                      to->incompression);
 +      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;
                }
  
                        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;