updates. New data types.
[silc.git] / apps / silcd / server.c
index 64e70fd16d7597710460572b33e2a9f8f6a445fb..3ef616134ee65a1d55860cbad94b33b55c2691e0 100644 (file)
@@ -155,7 +155,7 @@ int silc_server_init(SilcServer server)
   {
     unsigned char *public_key;
     unsigned char *private_key;
-    unsigned int pk_len, prv_len;
+    uint32 pk_len, prv_len;
     struct stat st;
 
     if (stat("pubkey.pub", &st) < 0 && stat("privkey.prv", &st) < 0) {
@@ -1912,7 +1912,7 @@ void silc_server_packet_parse_type(SilcServer server,
 /* Creates connection to a remote router. */
 
 void silc_server_create_connection(SilcServer server,
-                                  char *remote_host, unsigned int port)
+                                  char *remote_host, uint32 port)
 {
   SilcServerConnection sconn;
 
@@ -2087,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. */
@@ -2132,34 +2123,83 @@ 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;
+  uint32 clients_c = 0;
+  unsigned char **argv = NULL;
+  uint32 *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)) {
 
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
-       
+       if (client->data.registered == FALSE) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
+
        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,17 +2215,45 @@ int silc_server_remove_clients_by_server(SilcServer server,
     if (silc_idcache_list_first(list, &id_cache)) {
       while (id_cache) {
        client = (SilcClientEntry)id_cache->context;
+       if (client->data.registered == FALSE) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         else
+           continue;
+       }
        
        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))
@@ -2194,7 +2262,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;
 }
 
@@ -2266,11 +2367,13 @@ void silc_server_remove_from_channels(SilcServer server,
        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;
 
-       silc_free(channel->id);
        channel->id = NULL;
 
        silc_list_start(channel->user_list);
@@ -2313,11 +2416,13 @@ void silc_server_remove_from_channels(SilcServer server,
 
       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;
 
-       silc_free(channel->id);
        channel->id = NULL;
 
        silc_list_start(channel->user_list);
@@ -2349,7 +2454,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);
@@ -2394,6 +2499,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);
@@ -2425,11 +2532,13 @@ int silc_server_remove_from_one_channel(SilcServer server,
       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;
 
-       silc_free(channel->id);
        channel->id = NULL;
 
        silc_list_start(channel->user_list);
@@ -2622,17 +2731,34 @@ 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. */
 
 void silc_server_create_channel_key(SilcServer server, 
                                    SilcChannelEntry channel,
-                                   unsigned int key_len)
+                                   uint32 key_len)
 {
   int i;
   unsigned char channel_key[32], hash[32];
-  unsigned int len;
+  uint32 len;
 
   SILC_LOG_DEBUG(("Generating channel key"));
 
@@ -2676,6 +2802,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 
@@ -2689,7 +2831,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   SilcChannelKeyPayload payload = NULL;
   SilcChannelID *id = NULL;
   unsigned char *tmp, hash[32];
-  unsigned int tmp_len;
+  uint32 tmp_len;
   char *cipher;
 
   SILC_LOG_DEBUG(("Start"));
@@ -2767,6 +2909,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);
@@ -2930,7 +3087,7 @@ void silc_server_announce_clients(SilcServer server)
 }
 
 static SilcBuffer 
-silc_server_announce_encode_join(unsigned int argc, ...)
+silc_server_announce_encode_join(uint32 argc, ...)
 {
   va_list ap;
 
@@ -2988,7 +3145,7 @@ void silc_server_announce_get_channels(SilcServer server,
   SilcIDCacheEntry id_cache;
   SilcChannelEntry channel;
   unsigned char *cid;
-  unsigned short name_len;
+  uint16 name_len;
   int len;
 
   SILC_LOG_DEBUG(("Start"));
@@ -3105,13 +3262,13 @@ void silc_server_get_users_on_channel(SilcServer server,
                                      SilcChannelEntry channel,
                                      SilcBuffer *user_list,
                                      SilcBuffer *mode_list,
-                                     unsigned int *user_count)
+                                     uint32 *user_count)
 {
   SilcChannelClientEntry chl;
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   SilcBuffer idp;
-  unsigned int list_count = 0;
+  uint32 list_count = 0;
 
   client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) * 
                                     silc_list_count(channel->user_list));
@@ -3151,7 +3308,7 @@ void silc_server_save_users_on_channel(SilcServer server,
                                       SilcClientID *noadd,
                                       SilcBuffer user_list,
                                       SilcBuffer mode_list,
-                                      unsigned int user_count)
+                                      uint32 user_count)
 {
   int i;
 
@@ -3159,8 +3316,8 @@ void silc_server_save_users_on_channel(SilcServer server,
      whenever server sends notify message to channel. It means two things;
      some user has joined or leaved the channel. XXX TODO! */
   for (i = 0; i < user_count; i++) {
-    unsigned short idp_len;
-    unsigned int mode;
+    uint16 idp_len;
+    uint32 mode;
     SilcClientID *client_id;
     SilcClientEntry client;
 
@@ -3231,7 +3388,7 @@ void silc_server_save_users_on_channel(SilcServer server,
 
 SilcSocketConnection silc_server_get_client_route(SilcServer server,
                                                  unsigned char *id_data,
-                                                 unsigned int id_len,
+                                                 uint32 id_len,
                                                  SilcClientID *client_id,
                                                  SilcIDListData *idata)
 {
@@ -3313,7 +3470,7 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
   unsigned char *cid;
-  unsigned short name_len;
+  uint16 name_len;
   int len;
 
   silc_list_start(client->channels);