updates.
[silc.git] / apps / silcd / server.c
index 1e63e429fa0a09f65315830926f5043e6a9d2264..3003bbbff1fcf08e22671de803957248480f9081 100644 (file)
@@ -715,7 +715,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     silc_ske_free_key_material(ctx->keymat);
@@ -847,7 +848,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     if (ctx->dest_id)
       silc_free(ctx->dest_id);
@@ -1710,8 +1712,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1734,8 +1736,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
@@ -1765,8 +1767,8 @@ void silc_server_packet_parse_type(SilcServer server,
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
 
-    if (sock->protocol && sock->protocol->protocol->type 
-       == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+    if (sock->protocol && sock->protocol->protocol &&
+       sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
 
       SilcServerKEInternalContext *proto_ctx = 
        (SilcServerKEInternalContext *)sock->protocol->context;
@@ -2085,18 +2087,9 @@ void silc_server_free_sock_user_data(SilcServer server,
     {
       SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
 
-      /* Send REMOVE_ID packet to routers. */
-      if (!server->standalone && server->router)
-       silc_server_send_notify_server_signoff(server, 
-                                              server->router->connection,
-                                              server->server_type == 
-                                              SILC_SERVER ?
-                                              FALSE : TRUE, user_data->id, 
-                                              SILC_ID_SERVER_LEN);
-
-      /* Then also free all client entries that this server owns as
-        they will become invalid now as well. */
-      silc_server_remove_clients_by_server(server, user_data);
+      /* Free all client entries that this server owns as they will
+        become invalid now as well. */
+      silc_server_remove_clients_by_server(server, user_data, TRUE);
 
       /* If this was our primary router connection then we're lost to
         the outside world. */
@@ -2130,17 +2123,38 @@ void silc_server_free_sock_user_data(SilcServer server,
 
 /* This function is used to remove all client entries by the server `entry'.
    This is called when the connection is lost to the server. In this case
-   we must invalidate all the client entries owned by the server `entry'. */
+   we must invalidate all the client entries owned by the server `entry'. 
+   If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
+   distributed to our local clients. */
 
 int silc_server_remove_clients_by_server(SilcServer server, 
-                                        SilcServerEntry entry)
+                                        SilcServerEntry entry,
+                                        int server_signoff)
 {
   SilcIDCacheList list = NULL;
   SilcIDCacheEntry id_cache = NULL;
   SilcClientEntry client = NULL;
+  SilcBuffer idp;
+  SilcClientEntry *clients = NULL;
+  unsigned int clients_c = 0;
+  unsigned char **argv = NULL;
+  unsigned int *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  int i;
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (server_signoff) {
+    idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+    argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+    argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
+    argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
+    argv[argc] = idp->data;
+    argv_lens[argc] = idp->len;
+    argv_types[argc] = argc + 1;
+    argc++;
+    silc_buffer_free(idp);
+  }
+
   if (silc_idcache_find_by_id(server->local_list->clients, 
                              SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
 
@@ -2149,15 +2163,37 @@ int silc_server_remove_clients_by_server(SilcServer server,
        client = (SilcClientEntry)id_cache->context;
        
        if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
          if (!silc_idcache_list_next(list, &id_cache))
            break;
          else
            continue;
        }
 
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, TRUE, 
-                                        NULL, TRUE);
+       silc_server_remove_from_channels(server, NULL, client, FALSE, 
+                                        NULL, FALSE);
        silc_idlist_del_client(server->local_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2175,15 +2211,37 @@ int silc_server_remove_clients_by_server(SilcServer server,
        client = (SilcClientEntry)id_cache->context;
        
        if (client->router != entry) {
+         if (server_signoff && client->connection) {
+           clients = silc_realloc(clients, 
+                                  sizeof(*clients) * (clients_c + 1));
+           clients[clients_c] = client;
+           clients_c++;
+         }
+
          if (!silc_idcache_list_next(list, &id_cache))
            break;
          else
            continue;
        }
 
+       if (server_signoff) {
+         idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+         argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
+         argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
+                                  (argc + 1));
+         argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
+                                   (argc + 1));
+         argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+         memcpy(argv[argc], idp->data, idp->len);
+         argv_lens[argc] = idp->len;
+         argv_types[argc] = argc + 1;
+         argc++;
+         silc_buffer_free(idp);
+       }
+
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, TRUE,
-                                        NULL, TRUE);
+       silc_server_remove_from_channels(server, NULL, client, FALSE,
+                                        NULL, FALSE);
        silc_idlist_del_client(server->global_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2192,7 +2250,40 @@ int silc_server_remove_clients_by_server(SilcServer server,
     }
     silc_idcache_list_free(list);
   }
-  
+
+  /* Send the SERVER_SIGNOFF notify */
+  if (server_signoff) {
+    SilcBuffer args;
+
+    /* Send SERVER_SIGNOFF notify to our primary router */
+    if (!server->standalone && server->router) {
+      args = silc_argument_payload_encode(1, argv, argv_lens,
+                                         argv_types);
+      silc_server_send_notify_args(server, 
+                                  server->router->connection,
+                                  server->server_type == 
+                                  SILC_SERVER ? FALSE : TRUE, 
+                                  SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                                  argc, args);
+      silc_buffer_free(args);
+    }
+
+    args = silc_argument_payload_encode(argc, argv, argv_lens,
+                                       argv_types);
+    /* Send to local clients */
+    for (i = 0; i < clients_c; i++) {
+      silc_server_send_notify_args(server, clients[i]->connection,
+                                  FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+                                  argc, args);
+    }
+
+    silc_free(clients);
+    silc_buffer_free(args);
+    silc_free(argv);
+    silc_free(argv_lens);
+    silc_free(argv_types);
+  }
+
   return TRUE;
 }
 
@@ -2262,9 +2353,28 @@ void silc_server_remove_from_channels(SilcServer server,
     /* Remove channel if there is no users anymore */
     if (server->server_type == SILC_ROUTER &&
        silc_list_count(channel->user_list) < 2) {
+      server->stat.my_channels--;
+
+      if (channel->rekey)
+       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+
+       channel->id = NULL;
+
+       silc_list_start(channel->user_list);
+       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         silc_list_del(chl2->client->channels, chl2);
+         silc_list_del(channel->user_list, chl2);
+         silc_free(chl2);
+       }
+       continue;
+      }
+
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
       continue;
     }
 
@@ -2292,9 +2402,28 @@ void silc_server_remove_from_channels(SilcServer server,
                                           signoff_message, signoff_message ?
                                           strlen(signoff_message) : 0);
 
+      server->stat.my_channels--;
+
+      if (channel->rekey)
+       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+
+       channel->id = NULL;
+
+       silc_list_start(channel->user_list);
+       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         silc_list_del(chl2->client->channels, chl2);
+         silc_list_del(channel->user_list, chl2);
+         silc_free(chl2);
+       }
+       continue;
+      }
+
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
-      server->stat.my_channels--;
       continue;
     }
 
@@ -2313,7 +2442,7 @@ void silc_server_remove_from_channels(SilcServer server,
       silc_server_create_channel_key(server, channel, 0);
       
       /* Send the channel key to the channel. The key of course is not sent
-        to the client who was removed f rom the channel. */
+        to the client who was removed from the channel. */
       silc_server_send_channel_key(server, client->connection, channel, 
                                   server->server_type == SILC_ROUTER ? 
                                   FALSE : !server->standalone);
@@ -2358,6 +2487,8 @@ int silc_server_remove_from_one_channel(SilcServer server,
     /* Remove channel if there is no users anymore */
     if (server->server_type == SILC_ROUTER &&
        silc_list_count(channel->user_list) < 2) {
+      if (channel->rekey)
+       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
       silc_buffer_free(clidp);
@@ -2386,10 +2517,29 @@ int silc_server_remove_from_one_channel(SilcServer server,
                                           SILC_NOTIFY_TYPE_LEAVE, 1,
                                           clidp->data, clidp->len);
 
+      server->stat.my_channels--;
+      silc_buffer_free(clidp);
+
+      if (channel->rekey)
+       silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
+
+      if (channel->founder_key) {
+       /* The founder auth data exists, do not remove the channel entry */
+       SilcChannelClientEntry chl2;
+
+       channel->id = NULL;
+
+       silc_list_start(channel->user_list);
+       while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+         silc_list_del(chl2->client->channels, chl2);
+         silc_list_del(channel->user_list, chl2);
+         silc_free(chl2);
+       }
+       return FALSE;
+      }
+
       if (!silc_idlist_del_channel(server->local_list, channel))
        silc_idlist_del_channel(server->global_list, channel);
-      silc_buffer_free(clidp);
-      server->stat.my_channels--;
       return FALSE;
     }
 
@@ -2569,6 +2719,23 @@ silc_server_create_new_channel_with_id(SilcServer server,
   return entry;
 }
 
+/* Channel's key re-key timeout callback. */
+
+SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
+{
+  SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
+  SilcServer server = (SilcServer)rekey->context;
+
+  silc_server_create_channel_key(server, rekey->channel, rekey->key_len);
+  silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
+
+  silc_task_register(server->timeout_queue, 0, 
+                    silc_server_channel_key_rekey,
+                    (void *)rekey, 3600, 0,
+                    SILC_TASK_TIMEOUT,
+                    SILC_TASK_PRI_NORMAL);
+}
+
 /* Generates new channel key. This is used to create the initial channel key
    but also to re-generate new key for channel. If `key_len' is provided
    it is the bytes of the key length. */
@@ -2623,6 +2790,22 @@ void silc_server_create_channel_key(SilcServer server,
   silc_hash_make(channel->hmac->hash, channel->key, len, hash);
   silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
   memset(hash, 0, sizeof(hash));
+
+  if (server->server_type == SILC_ROUTER) {
+    if (!channel->rekey)
+      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    channel->rekey->context = (void *)server;
+    channel->rekey->channel = channel;
+    channel->rekey->key_len = key_len;
+
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_channel_key_rekey);
+    silc_task_register(server->timeout_queue, 0, 
+                      silc_server_channel_key_rekey,
+                      (void *)channel->rekey, 3600, 0,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_NORMAL);
+  }
 }
 
 /* Saves the channel key found in the encoded `key_payload' buffer. This 
@@ -2714,6 +2897,21 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   memset(hash, 0, sizeof(hash));
   memset(tmp, 0, tmp_len);
 
+  if (server->server_type == SILC_ROUTER) {
+    if (!channel->rekey)
+      channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    channel->rekey->context = (void *)server;
+    channel->rekey->channel = channel;
+
+    silc_task_unregister_by_callback(server->timeout_queue,
+                                    silc_server_channel_key_rekey);
+    silc_task_register(server->timeout_queue, 0, 
+                      silc_server_channel_key_rekey,
+                      (void *)channel->rekey, 3600, 0,
+                      SILC_TASK_TIMEOUT,
+                      SILC_TASK_PRI_NORMAL);
+  }
+
  out:
   if (id)
     silc_free(id);