Merged from silc_1_0_branch (second merge).
[silc.git] / apps / silcd / server.c
index 2a9b149ab2542400164e27241488a4c38368fe84..0c55b28d0e87f5c411bfdfe62069e4b15dd0deba 100644 (file)
@@ -30,7 +30,6 @@
 SILC_TASK_CALLBACK(silc_server_rehash_close_connection);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_retry);
 SILC_TASK_CALLBACK(silc_server_connect_router);
-SILC_TASK_CALLBACK(silc_server_connect_to_router);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
 SILC_TASK_CALLBACK(silc_server_accept_new_connection);
@@ -42,7 +41,6 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final);
 SILC_TASK_CALLBACK(silc_server_free_client_data_timeout);
 SILC_TASK_CALLBACK(silc_server_timeout_remote);
 SILC_TASK_CALLBACK(silc_server_channel_key_rekey);
-SILC_TASK_CALLBACK(silc_server_failure_callback);
 SILC_TASK_CALLBACK(silc_server_get_stats);
 
 /* Allocates a new SILC server object. This has to be done before the server
@@ -432,11 +430,7 @@ bool silc_server_init(SilcServer server)
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  silc_schedule_task_add(server->schedule, 0,
-                        silc_server_connect_to_router,
-                        (void *)server, 0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+  silc_server_create_connections(server);
 
   /* Add listener task to the scheduler. This task receives new connections
      to the server. This task remains on the queue until the end of the
@@ -697,12 +691,8 @@ bool silc_server_rehash(SilcServer server)
     }
   }
 
-  /* Go through all configured routers after rehash */
-  silc_schedule_task_add(server->schedule, 0,
-                        silc_server_connect_to_router,
-                        (void *)server, 0, 1,
-                        SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+  /* Create connections after rehash */
+  silc_server_create_connections(server);
 
   /* Check whether our router status has changed */
   if (newconfig->servers) {
@@ -1006,7 +996,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
    server to do authentication and key exchange with our router - called
    from schedule. */
 
-SILC_TASK_CALLBACK(silc_server_connect_to_router)
+SILC_TASK_CALLBACK_GLOBAL(silc_server_connect_to_router)
 {
   SilcServer server = (SilcServer)context;
   SilcServerConnection sconn;
@@ -1049,7 +1039,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       server->wait_backup = TRUE;
 
     if (ptr->initiator) {
-      /* Check whether we are connected to this host already */
+      /* Check whether we are connecting or connected to this host already */
       if (silc_server_num_sockets_by_remote(server, 
                                            silc_net_is_ip(ptr->host) ?
                                            ptr->host : NULL,
@@ -1059,6 +1049,15 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
        SILC_LOG_DEBUG(("We are already connected to this router"));
        continue;
       }
+      if (silc_server_num_sockets_by_remote(server, 
+                                           silc_net_is_ip(ptr->host) ?
+                                           ptr->host : NULL,
+                                           silc_net_is_ip(ptr->host) ?
+                                           NULL : ptr->host, ptr->port,
+                                           SILC_SOCKET_TYPE_UNKNOWN)) {
+       SILC_LOG_DEBUG(("We are already connecting to this router"));
+       continue;
+      }
 
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
@@ -1109,8 +1108,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, 
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
 
@@ -1154,8 +1151,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, 
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
 
@@ -1230,8 +1225,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     silc_free(sconn->remote_host);
     silc_free(sconn->backup_replace_ip);
     silc_free(sconn);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, 
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
     return;
@@ -1673,8 +1666,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     silc_server_config_unref(&ctx->sconfig);
     silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, 
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
                                  NULL);
@@ -1706,8 +1697,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
     silc_server_config_unref(&ctx->sconfig);
     silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, 
                                  SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
     server->stat.auth_failures++;
@@ -1795,8 +1784,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
     silc_server_config_unref(&ctx->sconfig);
     silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
-    silc_schedule_task_del_by_callback(server->schedule,
-                                      silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
                                  NULL);
     server->stat.auth_failures++;
@@ -2133,8 +2120,6 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
                        server->schedule);
 
  out:
-  silc_schedule_task_del_by_callback(server->schedule,
-                                    silc_server_failure_callback);
   silc_protocol_free(protocol);
   if (ctx->packet)
     silc_packet_context_free(ctx->packet);
@@ -2159,6 +2144,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
   SilcCipher cipher = NULL;
   SilcHmac hmac = NULL;
   SilcUInt32 sequence = 0;
+  bool local_is_router;
   int ret;
 
   if (!sock) {
@@ -2254,12 +2240,9 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
       else
        silc_server_free_sock_user_data(server, sock, NULL);
     } else if (server->router_conn && server->router_conn->sock == sock &&
-            !server->router && server->standalone)
-      silc_schedule_task_add(server->schedule, 0,
-                            silc_server_connect_to_router,
-                            server, 1, 0,
-                            SILC_TASK_TIMEOUT,
-                            SILC_TASK_PRI_NORMAL);
+              !server->router && server->standalone) {
+      silc_server_create_connections(server);
+    }
 
     silc_server_close_connection(server, sock);
     return;
@@ -2280,10 +2263,20 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     sequence = idata->psn_receive;
   }
 
-  /* Process the packet. This will call the parser that will then
+  /* Then, process the packet. This will call the parser that will then
      decrypt and parse the packet. */
-  ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
-                                   TRUE : FALSE, cipher, hmac, sequence,
+
+  local_is_router = (server->server_type == SILC_ROUTER);
+
+  /* If socket connection is our primary, we are backup and we are doing
+     backup resuming, we won't process the packet as being a router 
+     (affects channel message decryption). */
+  if (server->backup_router && SILC_SERVER_IS_BACKUP(sock) &&
+      SILC_PRIMARY_ROUTE(server) == sock)
+    local_is_router = FALSE;
+
+  ret = silc_packet_receive_process(sock, local_is_router,
+                                   cipher, hmac, sequence,
                                    silc_server_packet_parse, server);
 
   /* If processing failed the connection is closed. */
@@ -2294,6 +2287,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
       server->backup_noswitch = TRUE;
 
+    SILC_SET_DISCONNECTING(sock);
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock, NULL);
     silc_server_close_connection(server, sock);
@@ -2421,14 +2415,14 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
        and we want to call this processor with valid cipher. */
     if (idata)
       ret = silc_packet_receive_process(
-                                 sock, server->server_type == SILC_ROUTER ?
-                                 TRUE : FALSE, idata->receive_key,
+                                 sock, server->server_type == SILC_ROUTER,
+                                 idata->receive_key,
                                  idata->hmac_receive, idata->psn_receive,
                                  silc_server_packet_parse, server);
     else
       ret = silc_packet_receive_process(
-                                 sock, server->server_type == SILC_ROUTER ?
-                                 TRUE : FALSE, NULL, NULL, 0,
+                                 sock, server->server_type == SILC_ROUTER,
+                                 NULL, NULL, 0,
                                  silc_server_packet_parse, server);
 
     if (!ret) {
@@ -2438,6 +2432,7 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
       if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
        server->backup_noswitch = TRUE;
 
+      SILC_SET_DISCONNECTING(sock);
       if (sock->user_data)
        silc_server_free_sock_user_data(server, sock, NULL);
       silc_server_close_connection(server, sock);
@@ -2506,10 +2501,15 @@ void silc_server_packet_parse_type(SilcServer server,
                     message ? message : ""));
       silc_free(message);
 
+      /* Do not switch to backup in case of error */
+      server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
+
       /* Handle the disconnection from our end too */
+      SILC_SET_DISCONNECTING(sock);
       if (sock->user_data && SILC_IS_LOCAL(sock->user_data))
        silc_server_free_sock_user_data(server, sock, NULL);
       silc_server_close_connection(server, sock);
+      server->backup_noswitch = FALSE;
     }
     break;
 
@@ -2534,14 +2534,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
     if (sock->protocol) {
-      SilcServerFailureContext f;
-      f = silc_calloc(1, sizeof(*f));
-      f->sock = silc_socket_dup(sock);
-
-      /* We will wait 5 seconds to process this failure packet */
-      silc_schedule_task_add(server->schedule, sock->sock,
-                            silc_server_failure_callback, (void *)f, 5, 0,
-                            SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+      sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+      silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
     }
     break;
 
@@ -2940,7 +2934,10 @@ void silc_server_close_connection(SilcServer server,
   char tmp[128];
 
   if (!server->sockets[sock->sock] && SILC_IS_DISCONNECTED(sock)) {
-    silc_schedule_task_add(server->schedule, 0,
+    silc_schedule_unset_listen_fd(server->schedule, sock->sock);
+    silc_schedule_task_del_by_fd(server->schedule, sock->sock);
+    silc_net_close_connection(sock->sock);
+    silc_schedule_task_add(server->schedule, sock->sock,
                           silc_server_close_connection_final,
                           (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_NORMAL);
@@ -2956,14 +2953,9 @@ void silc_server_close_connection(SilcServer server,
                    sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
                    "Router"), tmp[0] ? tmp : ""));
 
-  /* We won't listen for this connection anymore */
-  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
-
   /* Unregister all tasks */
   silc_schedule_task_del_by_fd(server->schedule, sock->sock);
 
-  /* Close the actual connection */
-  silc_net_close_connection(sock->sock);
   server->sockets[sock->sock] = NULL;
 
   /* If sock->user_data is NULL then we'll check for active protocols
@@ -2972,7 +2964,8 @@ void silc_server_close_connection(SilcServer server,
   if (!sock->user_data) {
     /* If any protocol is active cancel its execution. It will call
        the final callback which will finalize the disconnection. */
-    if (sock->protocol) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
       SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
       silc_protocol_cancel(sock->protocol, server->schedule);
       sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -2982,7 +2975,13 @@ void silc_server_close_connection(SilcServer server,
     }
   }
 
-  silc_schedule_task_add(server->schedule, 0,
+  /* Close the actual connection */
+  silc_net_close_connection(sock->sock);
+
+  /* We won't listen for this connection anymore */
+  silc_schedule_unset_listen_fd(server->schedule, sock->sock);
+
+  silc_schedule_task_add(server->schedule, sock->sock,
                         silc_server_close_connection_final,
                         (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
                         SILC_TASK_PRI_NORMAL);
@@ -3001,8 +3000,13 @@ void silc_server_disconnect_remote(SilcServer server,
   char *cp;
   int len;
 
-  if (!sock || SILC_IS_DISCONNECTED(sock))
+  if (!sock)
+    return;
+
+  if (SILC_IS_DISCONNECTED(sock)) {
+    silc_server_close_connection(server, sock);
     return;
+  }
 
   memset(buf, 0, sizeof(buf));
   va_start(ap, status);
@@ -3160,15 +3164,11 @@ 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_schedule_task_add(server->schedule, 0,
-                                silc_server_connect_to_router,
-                                server, 1, 0,
-                                SILC_TASK_TIMEOUT,
-                                SILC_TASK_PRI_NORMAL);
-
+         silc_server_create_connections(server);
          server->id_entry->router = NULL;
          server->router = NULL;
          server->standalone = TRUE;
+         server->backup_primary = FALSE;
          backup_router = NULL;
        } else {
          if (server->id_entry != backup_router) {
@@ -3212,18 +3212,19 @@ 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_schedule_task_add(server->schedule, 0,
-                              silc_server_connect_to_router,
-                              server, 1, 0,
-                              SILC_TASK_TIMEOUT,
-                              SILC_TASK_PRI_NORMAL);
+       silc_server_create_connections(server);
       }
 
+      if (user_data->server_name)
+       SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+                              ("Server %s signoff", user_data->server_name));
+
       if (!backup_router) {
        /* Remove all servers that are originated from this server, and
           remove the clients of those servers too. */
        silc_server_remove_servers_by_server(server, user_data, TRUE);
 
+#if 0
        /* Remove the clients that this server owns as they will become
           invalid now too.  For backup router the server is actually
           coming from the primary router, so mark that as the owner
@@ -3233,6 +3234,7 @@ void silc_server_free_sock_user_data(SilcServer server,
          silc_server_remove_clients_by_server(server, server->router,
                                               user_data, TRUE);
        else
+#endif
          silc_server_remove_clients_by_server(server, user_data,
                                               user_data, TRUE);
 
@@ -3329,7 +3331,8 @@ void silc_server_free_sock_user_data(SilcServer server,
   }
 
   /* If any protocol is active cancel its execution */
-  if (sock->protocol) {
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
     SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
     silc_protocol_cancel(sock->protocol, server->schedule);
     sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -3575,7 +3578,8 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
 
   /* If we have protocol active we must assure that we call the protocol's
      final callback so that all the memory is freed. */
-  if (sock->protocol) {
+  if (sock->protocol && sock->protocol->protocol &&
+      sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) {
     protocol = sock->protocol->protocol->type;
     silc_protocol_cancel(sock->protocol, server->schedule);
     sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -3851,7 +3855,12 @@ bool silc_server_create_channel_key(SilcServer server,
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+    if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      channel->channel_key = NULL;
+      return FALSE;
+    }
   silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash);
   silc_hmac_set_key(channel->hmac, hash,
                    silc_hash_len(silc_hmac_get_hash(channel->hmac)));
@@ -3962,7 +3971,12 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
 
   /* Generate HMAC key from the channel key data and set it */
   if (!channel->hmac)
-    silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac);
+    if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      channel->channel_key = NULL;
+      return FALSE;
+    }
   silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
   silc_hmac_set_key(channel->hmac, hash,
                    silc_hash_len(silc_hmac_get_hash(channel->hmac)));
@@ -4590,27 +4604,6 @@ void silc_server_announce_channels(SilcServer server,
   silc_free(channel_ids);
 }
 
-/* Failure timeout callback. If this is called then we will immediately
-   process the received failure. We always process the failure with timeout
-   since we do not want to blindly trust to received failure packets.
-   This won't be called (the timeout is cancelled) if the failure was
-   bogus (it is bogus if remote does not close the connection after sending
-   the failure). */
-
-SILC_TASK_CALLBACK(silc_server_failure_callback)
-{
-  SilcServer server = app_context;
-  SilcServerFailureContext f = (SilcServerFailureContext)context;
-
-  if (f->sock->protocol) {
-    f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
-    silc_protocol_execute(f->sock->protocol, server->schedule, 0, 0);
-  }
-
-  silc_socket_free(f->sock);
-  silc_free(f);
-}
-
 /* Assembles user list and users mode list from the `channel'. */
 
 bool silc_server_get_users_on_channel(SilcServer server,
@@ -5083,7 +5076,8 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
-    SILC_LOG_ERROR(("Error occurred during rekey protocol"));
+    SILC_LOG_ERROR(("Error occurred during rekey protocol with "
+                   "%s (%s)", sock->hostname, sock->ip));
     silc_protocol_cancel(protocol, server->schedule);
     silc_protocol_free(protocol);
     sock->protocol = NULL;
@@ -5092,6 +5086,14 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     silc_free(ctx);
+
+    /* Reconnect */
+    SILC_SET_DISCONNECTING(sock);
+    server->backup_noswitch = TRUE;
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
+    silc_server_close_connection(server, sock);
+    silc_server_create_connections(server);
     return;
   }