Optimized the socket referencing in packet processing.
[silc.git] / apps / silcd / server.c
index 1f18368ec07d0e8a2170c502877a788a381e69b7..9860302b1b3cc192d70506d984dd54b31ca6d29f 100644 (file)
@@ -784,10 +784,12 @@ void silc_server_stop(SilcServer server)
        silc_server_disconnect_remote(server, server->sockets[i],
                                      SILC_STATUS_OK,
                                      "Server is shutting down");
-       if (sock->user_data)
-         silc_server_free_sock_user_data(server, sock,
-                                         "Server is shutting down");
-       silc_socket_free(sock);
+       if (server->sockets[i]) {
+         if (sock->user_data)
+           silc_server_free_sock_user_data(server, sock,
+                                           "Server is shutting down");
+         silc_socket_free(sock);
+       }
       } else {
        silc_socket_free(server->sockets[i]);
        server->sockets[i] = NULL;
@@ -1064,7 +1066,32 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_connect_to_router)
                                            NULL : ptr->host, ptr->port,
                                            SILC_SOCKET_TYPE_ROUTER)) {
        SILC_LOG_DEBUG(("We are already connected to this router"));
-       continue;
+
+       /* If we don't have primary router and this connection is our
+          primary router we are in desync.  Reconnect to the primary. */
+       if (server->standalone && !server->router) {
+         SilcServerConfigRouter *primary =
+           silc_server_config_get_primary_router(server);
+         if (primary == ptr) {
+           SilcSocketConnection sock =
+             silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
+                                             ptr->host, ptr->port);
+           if (sock) {
+             server->backup_noswitch = TRUE;
+             if (sock->user_data)
+               silc_server_free_sock_user_data(server, sock, NULL);
+             silc_server_disconnect_remote(server, sock, 0, NULL);
+             server->backup_noswitch = FALSE;
+             SILC_LOG_DEBUG(("Reconnecting to primary router"));
+           } else {
+             continue;
+           }
+         } else {
+           continue;
+         }
+       } else {
+         continue;
+       }
       }
       if (silc_server_num_sockets_by_remote(server,
                                            silc_net_is_ip(ptr->host) ?
@@ -1414,17 +1441,25 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
       server->router = id_entry;
       server->router->server_type = SILC_ROUTER;
       server->standalone = FALSE;
+      server->backup_primary = FALSE;
 
-      /* If we are router then announce our possible servers.  Backup
-        router announces also global servers. */
-      if (server->server_type == SILC_ROUTER)
-       silc_server_announce_servers(server,
-                                    server->backup_router ? TRUE : FALSE,
-                                    0, SILC_PRIMARY_ROUTE(server));
+      /* Announce data if we are not backup router (unless not as primary
+        currently).  Backup router announces later at the end of
+        resuming protocol. */
+      if (server->backup_router && server->server_type == SILC_ROUTER) {
+       SILC_LOG_DEBUG(("Announce data after resume protocol"));
+      } else {
+       /* If we are router then announce our possible servers.  Backup
+          router announces also global servers. */
+       if (server->server_type == SILC_ROUTER)
+         silc_server_announce_servers(server,
+                                      server->backup_router ? TRUE : FALSE,
+                                      0, SILC_PRIMARY_ROUTE(server));
 
-      /* Announce our clients and channels to the router */
-      silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
-      silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+       /* Announce our clients and channels to the router */
+       silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
+       silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+      }
 
       /* If we are backup router then this primary router is whom we are
         backing up. */
@@ -2167,7 +2202,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
                        server->schedule);
 
  out:
-  silc_protocol_free(protocol);
+  if (sock->protocol == protocol)
+    silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
@@ -2205,6 +2241,9 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     /* Do not send data to disconnected connection */
     if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
       SILC_LOG_DEBUG(("Disconnected socket connection, cannot send"));
+      SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
+      SILC_UNSET_OUTBUF_PENDING(sock);
+      silc_buffer_clear(sock->outbuf);
       return;
     }
 
@@ -2361,6 +2400,11 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
   SilcIDListData idata = (SilcIDListData)sock->user_data;
   int ret;
 
+  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+    SILC_LOG_DEBUG(("Connection is disconnected"));
+    goto out;
+  }
+
   server->stat.packets_received++;
 
   /* Parse the packet */
@@ -2464,6 +2508,11 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
     silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
 
+    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+      SILC_LOG_DEBUG(("Connection is disconnected"));
+      return FALSE;
+    }
+
     /* Reprocess data since we'll return FALSE here.  This is because
        the idata->receive_key might have become valid in the last packet
        and we want to call this processor with valid cipher. */
@@ -2511,8 +2560,6 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
     silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
                                  parser_context);
     break;
-  default:
-    return TRUE;
   }
 
   return TRUE;
@@ -2595,11 +2642,6 @@ void silc_server_packet_parse_type(SilcServer server,
      */
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
-    if (sock->protocol) {
-      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
-      break;
-    }
 
     /* Check for failure START_USE from backup router */
     if (server->server_type == SILC_SERVER &&
@@ -2609,9 +2651,38 @@ void silc_server_packet_parse_type(SilcServer server,
       if (type == SILC_SERVER_BACKUP_START_USE) {
        /* Attempt to reconnect to primary */
        SILC_LOG_DEBUG(("Received failed START_USE from backup %s", sock->ip));
+
+       /* Default action is to disconnect from backup and reconnect to
+          primary.  Since this failure can happen during switching to
+          backup (backup might have not noticed the primary going down yet),
+          we will wait a while and keep sending START_USE to backup.
+          Only after that we'll give up. */
+       if (server->router == sock->user_data &&
+           (time(0) - server->router_connect) < 30) {
+         SILC_LOG_DEBUG(("Resending START_USE to backup router"));
+         silc_server_backup_send_start_use(server, sock, FALSE);
+         break;
+       }
+
+       /* If backup is our primary, disconnect now. */
+       if (server->router == sock->user_data) {
+         if (sock->user_data)
+           silc_server_free_sock_user_data(server, sock, NULL);
+         SILC_SET_DISCONNECTING(sock);
+         silc_server_close_connection(server, sock);
+       }
+
+       /* Reconnect */
        silc_server_create_connections(server);
       }
     }
+
+    /* Execute protocol */
+    if (sock->protocol) {
+      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+      break;
+    }
     break;
 
   case SILC_PACKET_REJECT:
@@ -2723,7 +2794,12 @@ void silc_server_packet_parse_type(SilcServer server,
       silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2766,7 +2842,12 @@ void silc_server_packet_parse_type(SilcServer server,
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2809,7 +2890,12 @@ void silc_server_packet_parse_type(SilcServer server,
       }
     } else {
       SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2846,7 +2932,12 @@ void silc_server_packet_parse_type(SilcServer server,
       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -2945,7 +3036,12 @@ void silc_server_packet_parse_type(SilcServer server,
       silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     } else {
       SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
-                     "protocol active, packet dropped."));
+                     "protocol active (%s:%d [%s]).", sock->hostname,
+                     sock->port,
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     }
     break;
 
@@ -3053,6 +3149,7 @@ void silc_server_close_connection(SilcServer server,
                  "Router"), tmp[0] ? tmp : ""));
 
   SILC_SET_DISCONNECTED(sock);
+  silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
   silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_close_connection_final,
                         (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
@@ -3251,7 +3348,8 @@ void silc_server_free_sock_user_data(SilcServer server,
       if (server->router == user_data) {
        /* Check whether we have a backup router connection */
        if (!backup_router || backup_router == user_data) {
-         silc_server_create_connections(server);
+         if (!server->no_reconnect)
+           silc_server_create_connections(server);
          server->id_entry->router = NULL;
          server->router = NULL;
          server->standalone = TRUE;
@@ -3281,7 +3379,7 @@ void silc_server_free_sock_user_data(SilcServer server,
          /* We stop here to take a breath */
          sleep(2);
 
-         if (server->server_type == SILC_BACKUP_ROUTER) {
+         if (server->backup_router) {
            server->server_type = SILC_ROUTER;
 
            /* We'll need to constantly try to reconnect to the primary
@@ -3305,7 +3403,8 @@ void silc_server_free_sock_user_data(SilcServer server,
       } else if (server->server_type == SILC_SERVER &&
                 sock->type == SILC_SOCKET_TYPE_ROUTER) {
        /* Reconnect to the router (backup) */
-       silc_server_create_connections(server);
+       if (!server->no_reconnect)
+         silc_server_create_connections(server);
       }
 
       if (user_data->server_name)
@@ -3446,11 +3545,12 @@ void silc_server_remove_from_channels(SilcServer server,
   if (!client)
     return;
 
-  SILC_LOG_DEBUG(("Removing client from joined channels"));
-
   if (notify && !client->id)
     notify = FALSE;
 
+  SILC_LOG_DEBUG(("Removing client %s from joined channels",
+                 notify ? silc_id_render(client->id, SILC_ID_CLIENT) : ""));
+
   if (notify) {
     clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
     if (!clidp)
@@ -3499,7 +3599,7 @@ void silc_server_remove_from_channels(SilcServer server,
        !silc_server_channel_has_local(channel)) {
       /* Notify about leaving client if this channel has global users. */
       if (notify && channel->global_users)
-       silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+       silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                           SILC_NOTIFY_TYPE_SIGNOFF,
                                           signoff_message ? 2 : 1,
                                           clidp->data, clidp->len,
@@ -3513,7 +3613,7 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* Send notify to channel about client leaving SILC and channel too */
     if (notify)
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                         SILC_NOTIFY_TYPE_SIGNOFF,
                                         signoff_message ? 2 : 1,
                                         clidp->data, clidp->len,
@@ -3621,7 +3721,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
       !silc_server_channel_has_local(channel)) {
     /* Notify about leaving client if this channel has global users. */
     if (notify && channel->global_users)
-      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                         SILC_NOTIFY_TYPE_LEAVE, 1,
                                         clidp->data, clidp->len);
 
@@ -3633,7 +3733,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
 
   /* Send notify to channel about client leaving the channel */
   if (notify)
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+    silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
                                       SILC_NOTIFY_TYPE_LEAVE, 1,
                                       clidp->data, clidp->len);
 
@@ -4342,6 +4442,66 @@ void silc_server_announce_get_channel_topic(SilcServer server,
   }
 }
 
+/* Returns channel's invite and ban lists */
+
+void silc_server_announce_get_inviteban(SilcServer server,
+                                       SilcChannelEntry channel,
+                                       SilcBuffer *invite,
+                                       SilcBuffer *ban)
+{
+  SilcBuffer list, idp, idp2, tmp2;
+  SilcUInt32 type;
+  SilcHashTableList htl;
+  const unsigned char a[1] = { 0x03 };
+
+  idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+
+  /* Encode invite list */
+  if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+    list = silc_buffer_alloc_size(2);
+    type = silc_hash_table_count(channel->invite_list);
+    SILC_PUT16_MSB(type, list->data);
+    silc_hash_table_list(channel->invite_list, &htl);
+    while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+                                              type);
+    silc_hash_table_list_reset(&htl);
+
+    idp2 = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+    *invite =
+      silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_INVITE, 5,
+                                        idp->data, idp->len,
+                                        channel->channel_name,
+                                        strlen(channel->channel_name),
+                                        idp2->data, idp2->len,
+                                        a, 1,
+                                        list->data, list->len);
+    silc_buffer_free(idp2);
+    silc_buffer_free(list);
+  }
+
+  /* Encode ban list */
+  if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+    list = silc_buffer_alloc_size(2);
+    type = silc_hash_table_count(channel->ban_list);
+    SILC_PUT16_MSB(type, list->data);
+    silc_hash_table_list(channel->ban_list, &htl);
+    while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+      list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+                                              type);
+    silc_hash_table_list_reset(&htl);
+
+    *ban =
+      silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_BAN, 3,
+                                        idp->data, idp->len,
+                                        a, 1,
+                                        list->data, list->len);
+    silc_buffer_free(list);
+  }
+
+  silc_buffer_free(idp);
+}
+
 /* Returns assembled packets for channel users of the `channel'. */
 
 void silc_server_announce_get_channel_users(SilcServer server,
@@ -4461,6 +4621,8 @@ void silc_server_announce_get_channels(SilcServer server,
                                       SilcBuffer **channel_users_modes,
                                       SilcUInt32 *channel_users_modes_c,
                                       SilcBuffer **channel_topics,
+                                      SilcBuffer **channel_invites,
+                                      SilcBuffer **channel_bans,
                                       SilcChannelID ***channel_ids,
                                       unsigned long creation_time)
 {
@@ -4539,8 +4701,19 @@ void silc_server_announce_get_channels(SilcServer server,
          (*channel_topics)[i] = NULL;
          silc_server_announce_get_channel_topic(server, channel,
                                                 &(*channel_topics)[i]);
-         (*channel_users_modes_c)++;
 
+         /* Channel's invite and ban list */
+         *channel_invites = silc_realloc(*channel_invites,
+                                         sizeof(**channel_invites) * (i + 1));
+         (*channel_invites)[i] = NULL;
+         *channel_bans = silc_realloc(*channel_bans,
+                                      sizeof(**channel_bans) * (i + 1));
+         (*channel_bans)[i] = NULL;
+         silc_server_announce_get_inviteban(server, channel,
+                                            &(*channel_invites)[i],
+                                            &(*channel_bans)[i]);
+
+         (*channel_users_modes_c)++;
          silc_free(cid);
 
          i++;
@@ -4569,6 +4742,8 @@ void silc_server_announce_channels(SilcServer server,
   SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
   SilcBuffer *channel_topics = NULL;
+  SilcBuffer *channel_invites = NULL;
+  SilcBuffer *channel_bans = NULL;
   SilcUInt32 channel_users_modes_c = 0;
   SilcChannelID **channel_ids = NULL;
 
@@ -4581,6 +4756,8 @@ void silc_server_announce_channels(SilcServer server,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
                                    &channel_topics,
+                                   &channel_invites,
+                                   &channel_bans,
                                    &channel_ids, creation_time);
 
   /* Get channels and channel users in global list */
@@ -4591,6 +4768,8 @@ void silc_server_announce_channels(SilcServer server,
                                      &channel_users_modes,
                                      &channel_users_modes_c,
                                      &channel_topics,
+                                     &channel_invites,
+                                     &channel_bans,
                                      &channel_ids, creation_time);
 
   if (channels) {
@@ -4687,6 +4866,52 @@ void silc_server_announce_channels(SilcServer server,
     silc_free(channel_topics);
   }
 
+  if (channel_invites) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_invites[i])
+       continue;
+
+      silc_buffer_push(channel_invites[i],
+                      channel_invites[i]->data -
+                      channel_invites[i]->head);
+      SILC_LOG_HEXDUMP(("channel invite list"), channel_invites[i]->data,
+                      channel_invites[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_invites[i]->data,
+                                  channel_invites[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_invites[i]);
+    }
+    silc_free(channel_invites);
+  }
+
+  if (channel_bans) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_bans[i])
+       continue;
+
+      silc_buffer_push(channel_bans[i],
+                      channel_bans[i]->data -
+                      channel_bans[i]->head);
+      SILC_LOG_HEXDUMP(("channel ban list"), channel_bans[i]->data,
+                      channel_bans[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_bans[i]->data,
+                                  channel_bans[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_bans[i]);
+    }
+    silc_free(channel_bans);
+  }
+
   silc_free(channel_ids);
 }
 
@@ -5109,6 +5334,50 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   return buffer;
 }
 
+/* Timeout callback for unsuccessful rekey.  The rekey did not go through
+   for some reason. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_timeout)
+{
+  SilcServerRekeyInternalContext *ctx = context;
+  SilcServer server = app_context;
+  SilcSocketConnection sock = ctx->sock;
+
+  SILC_LOG_DEBUG(("Timeout occurred in rekey protocol with %s:%d [%s]",
+                 sock->hostname, sock->port,
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
+
+  SILC_LOG_WARNING(("Timeout occurred in rekey protocol with %s:%d [%s]",
+                   sock->hostname, sock->port,
+                   (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                    sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                    sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                    "Router")));
+
+  if (sock->protocol) {
+    silc_protocol_cancel(sock->protocol, server->schedule);
+    silc_protocol_free(sock->protocol);
+    sock->protocol = NULL;
+  }
+  if (ctx->packet)
+    silc_packet_context_free(ctx->packet);
+  if (ctx->ske)
+    silc_ske_free(ctx->ske);
+  silc_socket_free(sock);
+  silc_free(ctx);
+
+  /* Disconnect since we failed to rekey, the keys are probably wrong. */
+  silc_server_disconnect_remote(server, sock,
+                               SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+
+  /* Reconnect */
+  if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+    silc_server_create_connections(server);
+}
+
 /* A timeout callback for the re-key. We will be the initiator of the
    re-key protocol. */
 
@@ -5120,16 +5389,41 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_callback)
   SilcProtocol protocol;
   SilcServerRekeyInternalContext *proto_ctx;
 
+  if (!idata)
+    return;
+
+  /* Do not execute rekey with disabled connections, as it would not
+     go through anyway. */
+  if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+    return;
+
   /* If rekey protocol is active already wait for it to finish */
   if (sock->protocol && sock->protocol->protocol &&
       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)
     return;
 
+  /* If any other protocol is active do not start this protocol yet. */
+  if (sock->protocol) {
+    SILC_LOG_DEBUG(("Waiting for other protocol to finish before rekeying"));
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_rekey_callback,
+                          sock, 60, 0, SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s]",
+                 sock->hostname, sock->port,
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
+
   /* Allocate internal protocol context. This is sent as context
      to the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = (void *)server;
-  proto_ctx->sock = sock;
+  proto_ctx->sock = silc_socket_dup(sock);
   proto_ctx->responder = FALSE;
   proto_ctx->pfs = idata->rekey->pfs;
 
@@ -5139,6 +5433,18 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_callback)
                      &protocol, proto_ctx, silc_server_rekey_final);
   sock->protocol = protocol;
 
+  /* Register timeout callback in case the rekey does not go through. */
+  proto_ctx->timeout_task =
+    silc_schedule_task_add(server->schedule, sock->sock,
+                          silc_server_rekey_timeout,
+                          proto_ctx,
+                          (idata->rekey->timeout >
+                           server->config->key_exchange_timeout ?
+                           idata->rekey->timeout :
+                           server->config->key_exchange_timeout * 4), 0,
+                          SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
+
   /* Run the protocol */
   silc_protocol_execute(protocol, server->schedule, 0, 0);
 }
@@ -5155,6 +5461,9 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
   SilcSocketConnection sock = ctx->sock;
   SilcIDListData idata = (SilcIDListData)sock->user_data;
 
+  if (ctx->timeout_task)
+    silc_schedule_task_del(server->schedule, ctx->timeout_task);
+
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
@@ -5167,6 +5476,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
       silc_packet_context_free(ctx->packet);
     if (ctx->ske)
       silc_ske_free(ctx->ske);
+    silc_socket_free(sock);
     silc_free(ctx);
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
@@ -5177,7 +5487,12 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     return;
   }
 
-  SILC_LOG_DEBUG(("Rekey protocol completed"));
+  SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]",
+                 sock->hostname, sock->port,
+                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                  sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                  "Router")));
 
   /* Purge the outgoing data queue to assure that all rekey packets really
      go to the network before we quit the protocol. */
@@ -5197,6 +5512,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     silc_packet_context_free(ctx->packet);
   if (ctx->ske)
     silc_ske_free(ctx->ske);
+  silc_socket_free(sock);
   silc_free(ctx);
 }