Added preliminary backup router support for standalone routers.
[silc.git] / apps / silcd / packet_receive.c
index f91d0aa119c5eeac0ddb370147e982e9188299a5..a9f3f267f9300271910401ba816adc04f05c9224 100644 (file)
@@ -50,14 +50,11 @@ void silc_server_notify(SilcServer server,
   SilcUInt32 tmp_len;
   bool local;
 
-  SILC_LOG_DEBUG(("Start"));
-
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
-      packet->src_id_type != SILC_ID_SERVER)
-    return;
-
-  if (!packet->dst_id)
+      packet->src_id_type != SILC_ID_SERVER || !packet->dst_id) {
+    SILC_LOG_DEBUG(("Bad notify packet received"));
     return;
+  }
 
   /* If the packet is destined directly to a client then relay the packet
      before processing it. */
@@ -97,13 +94,12 @@ void silc_server_notify(SilcServer server,
       if (!channel_id)
        goto out;
 
-      if (!server->standalone)
-       silc_server_packet_send_dest(server, server->router->connection, 
-                                    packet->type, packet->flags | 
-                                    SILC_PACKET_FLAG_BROADCAST, 
-                                    channel_id, SILC_ID_CHANNEL,
-                                    packet->buffer->data, 
-                                    packet->buffer->len, FALSE);
+      silc_server_packet_send_dest(server, SILC_PRIMARY_ROUTE(server), 
+                                  packet->type, packet->flags | 
+                                  SILC_PACKET_FLAG_BROADCAST, 
+                                  channel_id, SILC_ID_CHANNEL,
+                                  packet->buffer->data, 
+                                  packet->buffer->len, FALSE);
       silc_server_backup_send_dest(server, (SilcServerEntry)sock->user_data, 
                                   packet->type, packet->flags,
                                   channel_id, SILC_ID_CHANNEL,
@@ -111,12 +107,11 @@ void silc_server_notify(SilcServer server,
                                   FALSE, TRUE);
     } else {
       /* Packet is destined to client or server */
-      if (!server->standalone)
-       silc_server_packet_send(server, server->router->connection, 
-                               packet->type,
-                               packet->flags | SILC_PACKET_FLAG_BROADCAST, 
-                               packet->buffer->data, packet->buffer->len, 
-                               FALSE);
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server), 
+                             packet->type,
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST, 
+                             packet->buffer->data, packet->buffer->len, 
+                             FALSE);
       silc_server_backup_send(server, (SilcServerEntry)sock->user_data,
                              packet->type, packet->flags,
                              packet->buffer->data, packet->buffer->len, 
@@ -151,6 +146,7 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -222,10 +218,17 @@ void silc_server_notify(SilcServer server,
     chl->client = client;
     chl->channel = channel;
 
-    /* If this is the first one on the channel then it is the founder of
-       the channel. */
-    if (!silc_hash_table_count(channel->user_list))
-      chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+    if (server->server_type != SILC_ROUTER ||
+       sock->type == SILC_SOCKET_TYPE_ROUTER) {
+      /* If this is the first one on the channel then it is the founder of
+        the channel. This is done on normal server and on router if this
+        notify is coming from router */
+      if (!silc_hash_table_count(channel->user_list)) {
+       SILC_LOG_DEBUG(("Client %s is founder on channel",
+                       silc_id_render(chl->client->id, SILC_ID_CLIENT)));
+       chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+      }
+    }
 
     silc_hash_table_add(channel->user_list, client, chl);
     silc_hash_table_add(client->channels, channel, chl);
@@ -262,6 +265,7 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -344,6 +348,7 @@ void silc_server_notify(SilcServer server,
       server->stat.cell_clients--;
     SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
     SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+    silc_schedule_task_del_by_context(server->schedule, client);
 
     /* Remove the client from all channels. */
     silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
@@ -353,6 +358,9 @@ void silc_server_notify(SilcServer server,
       silc_server_check_watcher_list(server, client, NULL,
                                     SILC_NOTIFY_TYPE_SIGNOFF);
 
+    /* Remove this client from watcher list if it is */
+    silc_server_del_from_watcher_list(server, client);
+
     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
     break;
@@ -408,13 +416,16 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
     }
 
-    if (channel->topic && !strcmp(channel->topic, tmp))
+    if (channel->topic && !strcmp(channel->topic, tmp)) {
+      SILC_LOG_DEBUG(("Topic is already set and same"));
       goto out;
+    }
 
     if (client) {
       /* Get user's channel entry and check that topic set is allowed. */
@@ -433,7 +444,7 @@ void silc_server_notify(SilcServer server,
     channel->topic = strdup(tmp);
 
     /* Send the same notify to the channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
+    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
                                       FALSE, packet->buffer->data, 
                                       packet->buffer->len, FALSE);
     silc_free(channel_id);
@@ -503,9 +514,9 @@ void silc_server_notify(SilcServer server,
     /* 
      * Distribute the notify to local clients on the channel
      */
-    
+
     SILC_LOG_DEBUG(("CMODE CHANGE notify"));
-      
+
     /* Get client ID */
     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
     if (!tmp)
@@ -543,6 +554,7 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -556,8 +568,42 @@ void silc_server_notify(SilcServer server,
     SILC_GET32_MSB(mode, tmp);
 
     /* Check if mode changed */
-    if (channel->mode == mode)
+    if (channel->mode == mode) {
+      SILC_LOG_DEBUG(("Mode is changed already"));
+
+      /* If this mode change has founder mode then we'll enforce the
+        change so that the server gets the real founder public key */
+      if (server->server_type != SILC_SERVER &&
+         sock != SILC_PRIMARY_ROUTE(server) &&
+         mode & SILC_CHANNEL_MODE_FOUNDER_AUTH && channel->founder_key) {
+       SILC_LOG_DEBUG(("Sending founder public key to server"));
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     channel->mode, server->id,
+                                     SILC_ID_SERVER, channel->cipher,
+                                     channel->hmac_name,
+                                     channel->passphrase,
+                                     channel->founder_key);
+      }
+
+      /* If we received same mode from our primary check whether founder
+        mode and key in the notify is set.  We update the founder key
+        here since we may have wrong one */
+      if (server->server_type == SILC_SERVER &&
+         sock == SILC_PRIMARY_ROUTE(server) &&
+         mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+       SILC_LOG_DEBUG(("Founder public key received from primary router"));
+       tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
+       if (!tmp)
+         break;
+
+       if (channel->founder_key)
+         silc_pkcs_public_key_free(channel->founder_key);
+       channel->founder_key = NULL;
+       silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
+      }
+
       break;
+    }
 
     /* Get user's channel entry and check that mode change is allowed */
     if (client) {
@@ -565,28 +611,52 @@ void silc_server_notify(SilcServer server,
        goto out;
       if (!silc_server_check_cmode_rights(server, channel, chl, mode)) {
        SILC_LOG_DEBUG(("CMODE change is not allowed"));
+       silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                     channel->mode, server->id,
+                                     SILC_ID_SERVER, channel->cipher,
+                                     channel->hmac_name,
+                                     channel->passphrase,
+                                     channel->founder_key);
        goto out;
       }
     } else {
+      /* Assure that server is not removing founder mode from us */
       if (server->server_type == SILC_ROUTER &&
+         sock != SILC_PRIMARY_ROUTE(server) &&
          channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH &&
          !(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
        SILC_LOG_DEBUG(("Enforcing sender to change channel mode"));
        silc_server_send_notify_cmode(server, sock, FALSE, channel,
                                      channel->mode, server->id,
-                                     SILC_ID_SERVER,
-                                     channel->cipher,
+                                     SILC_ID_SERVER, channel->cipher,
                                      channel->hmac_name,
                                      channel->passphrase,
                                      channel->founder_key);
        goto out;
       }
-    }
 
-    /* Send the same notify to the channel */
-    silc_server_packet_send_to_channel(server, sock, channel, packet->type, 
-                                      FALSE, packet->buffer->data, 
-                                      packet->buffer->len, FALSE);
+      /* If server is adding founder mode, check whether there is founder
+        on channel already and is not from this server */
+      if (server->server_type == SILC_ROUTER &&
+         sock != SILC_PRIMARY_ROUTE(server) &&
+         mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+       silc_hash_table_list(channel->user_list, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+         if (chl->mode & SILC_CHANNEL_UMODE_CHANFO &&
+             chl->client->router != sock->user_data) {
+           SILC_LOG_DEBUG(("Enforcing sender to change channel mode"));
+           silc_server_send_notify_cmode(server, sock, FALSE, channel,
+                                         channel->mode, server->id,
+                                         SILC_ID_SERVER, channel->cipher,
+                                         channel->hmac_name,
+                                         channel->passphrase,
+                                         channel->founder_key);
+           silc_hash_table_list_reset(&htl);
+           goto out;
+         }
+       silc_hash_table_list_reset(&htl);
+      }
+    }
 
     /* If the channel had private keys set and the mode was removed then
        we must re-generate and re-distribute a new channel key */
@@ -673,6 +743,11 @@ void silc_server_notify(SilcServer server,
                                    channel->passphrase, NULL);
     }
 
+    /* Send the same notify to the channel */
+    silc_server_packet_send_to_channel(server, NULL, channel, packet->type, 
+                                      FALSE, packet->buffer->data, 
+                                      packet->buffer->len, FALSE);
+
     /* Change mode */
     channel->mode = mode;
 
@@ -691,9 +766,9 @@ void silc_server_notify(SilcServer server,
        */
       SilcChannelClientEntry chl2 = NULL;
       bool notify_sent = FALSE;
-      
+
       SILC_LOG_DEBUG(("CUMODE CHANGE notify"));
-      
+
       /* Get client ID */
       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
       if (!tmp)
@@ -731,6 +806,7 @@ void silc_server_notify(SilcServer server,
        channel = silc_idlist_find_channel_by_id(server->local_list, 
                                                 channel_id, NULL);
        if (!channel) {
+         SILC_LOG_DEBUG(("Notify for unknown channel"));
          silc_free(channel_id);
          goto out;
        }
@@ -794,10 +870,15 @@ void silc_server_notify(SilcServer server,
       if (!silc_server_client_on_channel(client2, channel, &chl))
        goto out;
 
+      if (server->server_type == SILC_SERVER && chl->mode == mode) {
+       SILC_LOG_DEBUG(("Mode is changed already"));
+       break;
+      }
+
       if (mode & SILC_CHANNEL_UMODE_CHANFO &&
          !(chl->mode & SILC_CHANNEL_UMODE_CHANFO) &&
          server->server_type == SILC_ROUTER &&
-         sock->user_data != server->router) {
+         sock != SILC_PRIMARY_ROUTE(server)) {
        SilcPublicKey founder_key = NULL;
 
        /* If channel doesn't have founder auth mode then it's impossible
@@ -806,7 +887,7 @@ void silc_server_notify(SilcServer server,
           founder at all on the channel. */
        if (client && !(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
          /* Force the mode to not have founder mode */
-         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+         chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
          silc_server_force_cumode_change(server, sock, channel, chl, mode);
          notify_sent = TRUE;
          break;
@@ -817,10 +898,20 @@ void silc_server_notify(SilcServer server,
        silc_hash_table_list(channel->user_list, &htl);
        while (silc_hash_table_get(&htl, NULL, (void *)&chl2))
          if (chl2->mode & SILC_CHANNEL_UMODE_CHANFO) {
-           mode &= ~SILC_CHANNEL_UMODE_CHANFO;
-           silc_server_force_cumode_change(server, sock, channel,
-                                           chl, mode);
-           notify_sent = TRUE;
+           /* If the founder on the channel is not the one whom has set
+              the founder mode, then it's possible that this CUMODE_CHANGE
+              is correct.  Due to netsplits it's possible that this
+              situation happens. */
+           if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
+               (channel->founder_key && chl2->client->data.public_key &&
+                silc_pkcs_public_key_compare(
+                                       channel->founder_key,
+                                       chl2->client->data.public_key))) {
+             chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+             silc_server_force_cumode_change(server, sock, channel,
+                                             chl, mode);
+             notify_sent = TRUE;
+           }
            break;
          }
        silc_hash_table_list_reset(&htl);
@@ -831,30 +922,30 @@ void silc_server_notify(SilcServer server,
           is set on the channel now check whether this is the client that
           originally set the mode. */
 
-       /* Get public key that must be present in notify */
-       tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
-       if (!tmp || !silc_pkcs_public_key_decode(tmp, tmp_len,
-                                                &founder_key)) {
-         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
-         silc_server_force_cumode_change(server, sock, channel, chl, mode);
-         notify_sent = TRUE;
-         break;
-       }
-
        if (channel->founder_key) {
+         /* Get public key that must be present in notify */
+         tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
+         if (!tmp || !silc_pkcs_public_key_decode(tmp, tmp_len,
+                                                  &founder_key)) {
+           chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           silc_server_force_cumode_change(server, sock, channel, chl, mode);
+           notify_sent = TRUE;
+           break;
+         }
+
          /* Now match the public key we have cached and public key sent.
             They must match. */
          if (client && client->data.public_key && 
              !silc_pkcs_public_key_compare(channel->founder_key,
                                            client->data.public_key)) {
-           mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
            silc_server_force_cumode_change(server, sock, channel, chl, mode);
            notify_sent = TRUE;
            break;
          }
          if (!silc_pkcs_public_key_compare(channel->founder_key,
                                            founder_key)) {
-           mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+           chl->mode = mode &= ~SILC_CHANNEL_UMODE_CHANFO;
            silc_server_force_cumode_change(server, sock, channel, chl, mode);
            notify_sent = TRUE;
            break;
@@ -877,19 +968,21 @@ void silc_server_notify(SilcServer server,
          silc_pkcs_public_key_free(founder_key);
       }
 
-      if (server->server_type == SILC_ROUTER && chl->mode == mode) {
+      if (server->server_type != SILC_SERVER && chl->mode == mode) {
        SILC_LOG_DEBUG(("Mode is changed already"));
        break;
       }
 
-      SILC_LOG_DEBUG(("Changing the channel user mode"));
+      SILC_LOG_DEBUG(("Changing %s channel user mode",
+                     chl->client->nickname ? chl->client->nickname :
+                     (unsigned char *)""));
 
       /* Change the mode */
       chl->mode = mode;
 
       /* Send the same notify to the channel */
       if (!notify_sent)
-       silc_server_packet_send_to_channel(server, sock, channel, 
+       silc_server_packet_send_to_channel(server, NULL, channel, 
                                           packet->type, 
                                           FALSE, packet->buffer->data, 
                                           packet->buffer->len, FALSE);
@@ -920,6 +1013,7 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -1027,6 +1121,7 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->global_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -1072,6 +1167,13 @@ void silc_server_notify(SilcServer server,
       /* Re-announce our clients on the channel as the ID has changed now */
       silc_server_announce_get_channel_users(server, channel, &modes, &users,
                                             &users_modes);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
       if (modes) {
        silc_buffer_push(modes, modes->data - modes->head);
        silc_server_packet_send_dest(server, sock,
@@ -1080,13 +1182,6 @@ void silc_server_notify(SilcServer server,
                                     modes->data, modes->len, FALSE);
        silc_buffer_free(modes);
       }
-      if (users) {
-       silc_buffer_push(users, users->data - users->head);
-       silc_server_packet_send(server, sock,
-                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
-                               users->data, users->len, FALSE);
-       silc_buffer_free(users);
-      }
       if (users_modes) {
        silc_buffer_push(users_modes, users_modes->data - users_modes->head);
        silc_server_packet_send_dest(server, sock,
@@ -1199,9 +1294,14 @@ void silc_server_notify(SilcServer server,
     }
     silc_free(server_id);
 
+    /* Sending SERVER_SIGNOFF is not right way to signoff local connection */
+    if (SILC_IS_LOCAL(server_entry))
+      break;
+
     /* Free all client entries that this server owns as they will
        become invalid now as well. */
     silc_server_remove_clients_by_server(server, server_entry, TRUE);
+    silc_server_backup_del(server, server_entry);
 
     /* Remove the server entry */
     silc_idlist_del_server(local ? server->local_list :
@@ -1234,6 +1334,7 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -1327,10 +1428,10 @@ void silc_server_notify(SilcServer server,
 
       /* If the the client is not in local list we check global list */
       client = silc_idlist_find_client_by_id(server->global_list, 
-                                            client_id, TRUE, NULL);
+                                            client_id, TRUE, &cache);
       if (!client) {
        client = silc_idlist_find_client_by_id(server->local_list, 
-                                              client_id, TRUE, NULL);
+                                              client_id, TRUE, &cache);
        if (!client) {
          silc_free(client_id);
          goto out;
@@ -1374,7 +1475,8 @@ void silc_server_notify(SilcServer server,
          silc_free(client_id);
 
          /* Killer must be router operator */
-         if (!(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+         if (server->server_type != SILC_SERVER &&
+             !(client2->mode & SILC_UMODE_ROUTER_OPERATOR)) {
            SILC_LOG_DEBUG(("Killing is not allowed"));
            goto out;
          }
@@ -1393,10 +1495,25 @@ void silc_server_notify(SilcServer server,
                                       FALSE);
 
       /* Check if anyone is watching this nickname */
-      if (server->server_type == SILC_ROUTER)
-       silc_server_check_watcher_list(server, client, NULL,
-                                      SILC_NOTIFY_TYPE_KILLED);
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_KILLED);
+
+      /* Update statistics */
+      server->stat.clients--;
+      if (server->stat.cell_clients)
+       server->stat.cell_clients--;
+      SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+      SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
 
+      if (SILC_IS_LOCAL(client)) {
+       server->stat.my_clients--;
+       silc_schedule_task_del_by_context(server->schedule, client);
+       silc_idlist_del_data(client);
+       client->mode = 0;
+      }
+
+      client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+      cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
       break;
     }
 
@@ -1494,6 +1611,7 @@ void silc_server_notify(SilcServer server,
       channel = silc_idlist_find_channel_by_id(server->local_list, 
                                               channel_id, NULL);
       if (!channel) {
+       SILC_LOG_DEBUG(("Notify for unknown channel"));
        silc_free(channel_id);
        goto out;
       }
@@ -2173,11 +2291,9 @@ SilcClientEntry silc_server_new_client(SilcServer server,
                   client_id, client, 0, NULL);
 
   /* Notify our router about new client on the SILC network */
-  if (!server->standalone)
-    silc_server_send_new_id(server, (SilcSocketConnection) 
-                           server->router->connection, 
-                           server->server_type == SILC_ROUTER ? TRUE : FALSE,
-                           client->id, SILC_ID_CLIENT, id_len);
+  silc_server_send_new_id(server, SILC_PRIMARY_ROUTE(server),
+                         SILC_BROADCAST(server), client->id,
+                         SILC_ID_CLIENT, id_len);
 
   /* Distribute to backup routers */
   if (server->server_type == SILC_ROUTER) {
@@ -2334,8 +2450,8 @@ SilcServerEntry silc_server_new_server(SilcServer server,
      to our router. If we are normal server we won't send anything
      since this connection must be our router connection. */
   if (server->server_type == SILC_ROUTER && !server->standalone &&
-      server->router->connection != sock)
-    silc_server_send_new_id(server, server->router->connection,
+      SILC_PRIMARY_ROUTE(server) != sock)
+    silc_server_send_new_id(server, SILC_PRIMARY_ROUTE(server),
                            TRUE, new_server->id, SILC_ID_SERVER, 
                            silc_id_get_len(server_id, SILC_ID_SERVER));
 
@@ -2380,6 +2496,12 @@ SilcServerEntry silc_server_new_server(SilcServer server,
       silc_server_announce_clients(server, 0, sock);
       silc_server_announce_channels(server, 0, sock);
     }
+
+    /* By default the servers connected to backup router are disabled
+       until backup router has become the primary */
+    if (server->server_type == SILC_BACKUP_ROUTER &&
+       sock->type == SILC_SOCKET_TYPE_SERVER)
+      idata->status |= SILC_IDLIST_STATUS_DISABLED;
   }
 
   return new_server;
@@ -2438,7 +2560,7 @@ static void silc_server_new_id_real(SilcServer server,
        id_type == SILC_ID_SERVER && 
        server->id_entry->server_type != SILC_BACKUP_ROUTER) {
       id_list = server->global_list;
-      router_sock = server->router ? server->router->connection : sock;
+      router_sock = server->router ? SILC_PRIMARY_ROUTE(server) : sock;
     }
   } else {
     void *sender_id = silc_id_str2id(packet->src_id, packet->src_id_len,
@@ -2571,11 +2693,10 @@ static void silc_server_new_id_real(SilcServer server,
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received New ID packet"));
-    if (!server->standalone)
-      silc_server_packet_send(server, server->router->connection,
-                             packet->type, 
-                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                             buffer->data, buffer->len, FALSE);
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           buffer->data, buffer->len, FALSE);
     silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                            packet->type, packet->flags,
                            packet->buffer->data, packet->buffer->len, 
@@ -2619,12 +2740,11 @@ void silc_server_new_id_list(SilcServer server, SilcSocketConnection sock,
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received New ID List packet"));
-    if (!server->standalone)
-      silc_server_packet_send(server, server->router->connection,
-                             packet->type, 
-                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                             packet->buffer->data, 
-                             packet->buffer->len, FALSE);
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data, 
+                           packet->buffer->len, FALSE);
     silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                            packet->type, packet->flags,
                            packet->buffer->data, packet->buffer->len, 
@@ -2684,8 +2804,6 @@ void silc_server_new_channel(SilcServer server,
   SilcServerEntry server_entry;
   SilcChannelEntry channel;
 
-  SILC_LOG_DEBUG(("Processing New Channel"));
-
   if (sock->type == SILC_SOCKET_TYPE_CLIENT ||
       packet->src_id_type != SILC_ID_SERVER ||
       server->server_type == SILC_SERVER)
@@ -2757,6 +2875,8 @@ void silc_server_new_channel(SilcServer server,
     /* If the channel does not exist, then create it. This creates a new
        key to the channel as well that we will send to the server. */
     if (!channel) {
+      SILC_LOG_DEBUG(("Channel is new to us"));
+
       /* The protocol says that the Channel ID's IP address must be based
         on the router's IP address.  Check whether the ID is based in our
         IP and if it is not then create a new ID and enforce the server
@@ -2784,6 +2904,7 @@ void silc_server_new_channel(SilcServer server,
        return;
       }
       channel->disabled = TRUE;
+      channel->mode = silc_channel_get_mode(payload);
 
       /* Send the new channel key to the server */
       id = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
@@ -2803,6 +2924,8 @@ void silc_server_new_channel(SilcServer server,
         We also create a new key for the channel. */
       SilcBuffer modes = NULL, users = NULL, users_modes = NULL;
 
+      SILC_LOG_DEBUG(("Channel already exists"));
+
       if (!SILC_ID_CHANNEL_COMPARE(channel_id, channel->id)) {
        /* They don't match, send CHANNEL_CHANGE notify to the server to
           force the ID change. */
@@ -2814,8 +2937,8 @@ void silc_server_new_channel(SilcServer server,
        return;
       }
 
-#if 0 /* Lets expect that server send CMODE_CHANGE notify anyway to
-        (attempt) force mode change, and may very well get it. */
+#if 0 /* We will announce our CMODE anyway for this channel, so no need
+        to check it (implicit enforce). */
 
       /* If the mode is different from what we have then enforce the
         mode change. */
@@ -2867,6 +2990,13 @@ void silc_server_new_channel(SilcServer server,
         users on the channel "joining" the channel. */
       silc_server_announce_get_channel_users(server, channel, &modes, &users,
                                             &users_modes);
+      if (users) {
+       silc_buffer_push(users, users->data - users->head);
+       silc_server_packet_send(server, sock,
+                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                               users->data, users->len, FALSE);
+       silc_buffer_free(users);
+      }
       if (modes) {
        silc_buffer_push(modes, modes->data - modes->head);
        silc_server_packet_send_dest(server, sock,
@@ -2875,13 +3005,6 @@ void silc_server_new_channel(SilcServer server,
                                     modes->data, modes->len, FALSE);
        silc_buffer_free(modes);
       }
-      if (users) {
-       silc_buffer_push(users, users->data - users->head);
-       silc_server_packet_send(server, sock,
-                               SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
-                               users->data, users->len, FALSE);
-       silc_buffer_free(users);
-      }
       if (users_modes) {
        silc_buffer_push(users_modes, users_modes->data - users_modes->head);
        silc_server_packet_send_dest(server, sock,
@@ -2929,12 +3052,11 @@ void silc_server_new_channel_list(SilcServer server,
       sock->type == SILC_SOCKET_TYPE_SERVER &&
       !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
     SILC_LOG_DEBUG(("Broadcasting received New Channel List packet"));
-    if (!server->standalone)
-      silc_server_packet_send(server, server->router->connection,
-                             packet->type, 
-                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                             packet->buffer->data, 
-                             packet->buffer->len, FALSE);
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           packet->type, 
+                           packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                           packet->buffer->data, 
+                           packet->buffer->len, FALSE);
     silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                            packet->type, packet->flags,
                            packet->buffer->data, packet->buffer->len, 
@@ -3161,6 +3283,8 @@ SILC_SERVER_CMD_FUNC(resume_resolve)
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                  "Resuming not possible");
+    if (sock->user_data)
+      silc_server_free_sock_user_data(server, sock, NULL);
     goto out;
   }
 
@@ -3177,6 +3301,8 @@ SILC_SERVER_CMD_FUNC(resume_resolve)
        silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                      "Resuming not possible");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
        goto out;
       }
     }
@@ -3187,6 +3313,8 @@ SILC_SERVER_CMD_FUNC(resume_resolve)
       silc_server_disconnect_remote(server, sock,
                                    SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                    "Resuming not possible");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
       goto out;
     }
   }
@@ -3223,8 +3351,6 @@ void silc_server_resume_client(SilcServer server,
   SilcChannelClientEntry chl;
   SilcServerResumeResolve r;
 
-  SILC_LOG_DEBUG(("Start"));
-
   ret = silc_buffer_unformat(buffer,
                             SILC_STR_UI16_NSTRING(&id_string, &id_len),
                             SILC_STR_END);
@@ -3249,6 +3375,9 @@ void silc_server_resume_client(SilcServer server,
       silc_server_disconnect_remote(server, sock,
                                    SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                    "Resuming not possible");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_free(client_id);
       return;
     }
 
@@ -3270,7 +3399,7 @@ void silc_server_resume_client(SilcServer server,
        r->server = server;
        r->sock = silc_socket_dup(sock);
        r->packet = silc_packet_context_dup(packet);
-       r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
+       r->data = client_id;
        silc_server_command_pending(server, SILC_COMMAND_WHOIS,
                                    server->cmd_ident,
                                    silc_server_command_resume_resolve, r);
@@ -3280,6 +3409,9 @@ void silc_server_resume_client(SilcServer server,
        silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                      "Resuming not possible");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       silc_free(client_id);
       }
       return;
     }
@@ -3304,7 +3436,7 @@ void silc_server_resume_client(SilcServer server,
        r->server = server;
        r->sock = silc_socket_dup(sock);
        r->packet = silc_packet_context_dup(packet);
-       r->data = silc_id_dup(client_id, SILC_ID_CLIENT);
+       r->data = client_id;
        silc_server_command_pending(server, SILC_COMMAND_WHOIS,
                                    server->cmd_ident,
                                    silc_server_command_resume_resolve, r);
@@ -3316,6 +3448,9 @@ void silc_server_resume_client(SilcServer server,
        silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                      "Resuming not possible");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       silc_free(client_id);
        return;
       }
     }
@@ -3324,9 +3459,14 @@ void silc_server_resume_client(SilcServer server,
        resolve it first. */
     if (!detached_client->data.public_key) {
       if (server->server_type == SILC_SERVER && server->standalone) {
+       SILC_LOG_ERROR(("Detached client's public key not present, "
+                       "closing connection"));
        silc_server_disconnect_remote(server, sock,
                                      SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                      "Resuming not possible");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock, NULL);
+       silc_free(client_id);
       } else {
        /* We must retrieve the detached client's public key by sending
           GETKEY command. Reprocess this packet after receiving the key */
@@ -3337,13 +3477,15 @@ void silc_server_resume_client(SilcServer server,
        SILC_LOG_DEBUG(("Resolving client public key"));
 
        silc_server_send_command(server, dest_sock ? dest_sock : 
-                                server->router->connection,
+                                SILC_PRIMARY_ROUTE(server),
                                 SILC_COMMAND_GETKEY, ++server->cmd_ident,
                                 1, 1, idp->data, idp->len);
 
        r = silc_calloc(1, sizeof(*r));
-       if (!r)
+       if (!r) {
+         silc_free(client_id);
          return;
+       }
 
        r->server = server;
        r->sock = silc_socket_dup(sock);
@@ -3354,14 +3496,20 @@ void silc_server_resume_client(SilcServer server,
 
        silc_buffer_free(idp);
       }
+      silc_free(client_id);
       return;
     } else if (!silc_pkcs_public_key_compare(detached_client->data.public_key,
                                             idata->public_key)) {
       /* We require that the connection and resuming authentication data
         must be using same key pair. */
+      SILC_LOG_ERROR(("Resuming attempted with wrong public key, "
+                     "closing connection"));
       silc_server_disconnect_remote(server, sock,
                                    SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                    "Resuming not possible");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_free(client_id);
       return;
     }
 
@@ -3377,6 +3525,9 @@ void silc_server_resume_client(SilcServer server,
       silc_server_disconnect_remote(server, sock,
                                    SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                    "Resuming not possible");
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_free(client_id);
       return;
     }
 
@@ -3403,10 +3554,9 @@ void silc_server_resume_client(SilcServer server,
                       SILC_STR_END);
 
     /* Send to primary router */
-    if (!server->standalone)
-      silc_server_packet_send(server, server->router->connection,
-                             SILC_PACKET_RESUME_CLIENT, 0, 
-                             buf->data, buf->len, TRUE);
+    silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                           SILC_PACKET_RESUME_CLIENT, 0, 
+                           buf->data, buf->len, TRUE);
 
     /* As router we must deliver this packet directly to the original
        server whom this client was earlier. */
@@ -3430,6 +3580,7 @@ void silc_server_resume_client(SilcServer server,
 
     /* If the ID is not based in our ID then change it */
     if (!SILC_ID_COMPARE(client->id, server->id, server->id->ip.data_len)) {
+      silc_free(client_id);
       while (!silc_id_create_client_id(server, server->id, server->rng, 
                                       server->md5hash, client->nickname, 
                                       &client_id)) {
@@ -3437,6 +3588,8 @@ void silc_server_resume_client(SilcServer server,
        if (nickfail > 9) {
          silc_server_disconnect_remote(server, sock, 
                                        SILC_STATUS_ERR_BAD_NICKNAME, NULL);
+         if (sock->user_data)
+           silc_server_free_sock_user_data(server, sock, NULL);
          return;
        }
        snprintf(&client->nickname[strlen(client->nickname) - 1], 1, 
@@ -3447,10 +3600,10 @@ void silc_server_resume_client(SilcServer server,
 
     if (nick_change) {
       /* Notify about Client ID change, nickname doesn't actually change. */
-      if (!server->standalone)
-       silc_server_send_notify_nick_change(server, server->router->connection,
-                                           FALSE, client->id, client_id,
-                                           client->nickname);
+      silc_server_send_notify_nick_change(server, SILC_PRIMARY_ROUTE(server),
+                                         SILC_BROADCAST(server),
+                                         client->id, client_id,
+                                         client->nickname);
     }
 
     /* Resolve users on those channels that client has joined but we
@@ -3462,7 +3615,7 @@ void silc_server_resume_client(SilcServer server,
        SILC_LOG_DEBUG(("Resolving users for %s channel", 
                        channel->channel_name));
        if (channel->disabled || !channel->users_resolved) {
-         silc_server_send_command(server, server->router->connection,
+         silc_server_send_command(server, SILC_PRIMARY_ROUTE(server),
                                   SILC_COMMAND_USERS, ++server->cmd_ident,
                                   1, 2, channel->channel_name,
                                   strlen(channel->channel_name));
@@ -3534,9 +3687,8 @@ void silc_server_resume_client(SilcServer server,
       silc_server_packet_send(server, sock, SILC_PACKET_CHANNEL_KEY, 0, 
                              keyp->data, keyp->len, FALSE);
 
-      if (created && server->server_type == SILC_SERVER && 
-         !server->standalone)
-       silc_server_packet_send(server, server->router->connection, 
+      if (created && server->server_type == SILC_SERVER)
+       silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server), 
                                SILC_PACKET_CHANNEL_KEY, 0, 
                                keyp->data, keyp->len, FALSE);
 
@@ -3550,8 +3702,10 @@ void silc_server_resume_client(SilcServer server,
     SilcServerEntry server_entry;
     SilcServerID *server_id;
 
-    if (!client_id)
+    if (!client_id) {
+      SILC_LOG_DEBUG(("Malformed resuming packet"));
       return;
+    }
 
     /* Get entry to the client, and resolve it if we don't have it. */
     detached_client = silc_idlist_find_client_by_id(server->local_list, 
@@ -3559,8 +3713,11 @@ void silc_server_resume_client(SilcServer server,
     if (!detached_client) {
       detached_client = silc_idlist_find_client_by_id(server->global_list,
                                                      client_id, TRUE, NULL);
-      if (!detached_client)
+      if (!detached_client) {
+       SILC_LOG_DEBUG(("Resuming client is unknown"));
+       silc_free(client_id);
        return;
+      }
     }
 
     /* Check that the client has not been resumed already because it is
@@ -3571,14 +3728,19 @@ void silc_server_resume_client(SilcServer server,
       /* The client is clearly attempting to resume more than once and
         perhaps playing around by resuming from several different places
         at the same time. */
+      SILC_LOG_DEBUG(("Attempting to re-resume client, killing both"));
       silc_server_kill_client(server, detached_client, NULL,
                              server->id, SILC_ID_SERVER);
+      silc_free(client_id);
       return;
     }
 
     /* Check whether client is detached at all */
-    if (!(detached_client->mode & SILC_UMODE_DETACHED))
+    if (!(detached_client->mode & SILC_UMODE_DETACHED)) {
+      SILC_LOG_DEBUG(("Client is not detached"));
+      silc_free(client_id);
       return;
+    }
 
     /* Client is detached, and now it is resumed.  Remove the detached
        mode and mark that it is resumed. */
@@ -3588,8 +3750,10 @@ void silc_server_resume_client(SilcServer server,
     /* Get the new owner of the resumed client */
     server_id = silc_id_str2id(packet->src_id, packet->src_id_len,
                               packet->src_id_type);
-    if (!server_id)
+    if (!server_id) {
+      silc_free(client_id);
       return;
+    }
 
     /* Get server entry */
     server_entry = silc_idlist_find_server_by_id(server->global_list, 
@@ -3601,6 +3765,7 @@ void silc_server_resume_client(SilcServer server,
       local = FALSE;
       if (!server_entry) {
        silc_free(server_id);
+       silc_free(client_id);
        return;
       }
     }
@@ -3622,6 +3787,7 @@ void silc_server_resume_client(SilcServer server,
                     server->global_list->clients, 
                     detached_client->nickname,
                     detached_client->id, detached_client, FALSE, NULL);
+    detached_client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
 
     /* Change the owner of the client if needed */
     if (detached_client->router != server_entry)
@@ -3644,11 +3810,10 @@ void silc_server_resume_client(SilcServer server,
        sock->type == SILC_SOCKET_TYPE_SERVER &&
        !(packet->flags & SILC_PACKET_FLAG_BROADCAST)) {
       SILC_LOG_DEBUG(("Broadcasting received Resume Client packet"));
-      if (!server->standalone)
-       silc_server_packet_send(server, server->router->connection,
-                               packet->type, 
-                               packet->flags | SILC_PACKET_FLAG_BROADCAST,
-                               buffer->data, buffer->len, FALSE);
+      silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+                             packet->type, 
+                             packet->flags | SILC_PACKET_FLAG_BROADCAST,
+                             buffer->data, buffer->len, FALSE);
       silc_server_backup_send(server, (SilcServerEntry)sock->user_data, 
                              packet->type, packet->flags,
                              packet->buffer->data, packet->buffer->len,