Added support for multiple PublicKey instances in the config
[silc.git] / apps / silcd / server_util.c
index a29c2fd3757685017be70b928ae0368b528ea117..dc76c48161f642ac13932a06fc0148f71bbe463e 100644 (file)
@@ -46,7 +46,7 @@ static void silc_server_remove_clients_channels(SilcServer server,
   /* Remove the client from all channels. The client is removed from
      the channels' user list. */
   silc_hash_table_list(client->channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+  while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
     channel = chl->channel;
 
     /* Remove channel from client's channel list */
@@ -71,6 +71,7 @@ static void silc_server_remove_clients_channels(SilcServer server,
 
     /* Remove client from channel's client list */
     silc_hash_table_del(channel->user_list, chl->client);
+    channel->user_count--;
 
     /* If there is no global users on the channel anymore mark the channel
        as local channel. Do not check if the removed client is local client. */
@@ -100,11 +101,13 @@ static void silc_server_remove_clients_channels(SilcServer server,
        channel->disabled = TRUE;
 
        silc_hash_table_list(channel->user_list, &htl2);
-       while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+       while (silc_hash_table_get(&htl2, NULL, (void **)&chl2)) {
          silc_hash_table_del(chl2->client->channels, channel);
          silc_hash_table_del(channel->user_list, chl2->client);
+         channel->user_count--;
          silc_free(chl2);
        }
+       silc_hash_table_list_reset(&htl2);
        continue;
       }
 
@@ -121,6 +124,7 @@ static void silc_server_remove_clients_channels(SilcServer server,
     if (!silc_hash_table_find(channels, channel, NULL, NULL))
       silc_hash_table_add(channels, channel, channel);
   }
+  silc_hash_table_list_reset(&htl);
 
   silc_buffer_free(clidp);
 }
@@ -140,9 +144,9 @@ bool silc_server_remove_clients_by_server(SilcServer server,
   SilcClientEntry client = NULL;
   SilcBuffer idp;
   SilcClientEntry *clients = NULL;
-  uint32 clients_c = 0;
+  SilcUInt32 clients_c = 0;
   unsigned char **argv = NULL;
-  uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  SilcUInt32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
   SilcHashTableList htl;
   SilcChannelEntry channel;
   SilcHashTable channels;
@@ -210,13 +214,21 @@ bool silc_server_remove_clients_by_server(SilcServer server,
          silc_buffer_free(idp);
        }
 
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
-       id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       /* Update statistics */
        server->stat.clients--;
        if (server->server_type == SILC_ROUTER)
          server->stat.cell_clients--;
+       SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+       SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       if (!server_signoff) {
+         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       } else {
+         silc_idlist_del_client(server->local_list, client);
+       }
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
@@ -266,13 +278,21 @@ bool silc_server_remove_clients_by_server(SilcServer server,
          silc_buffer_free(idp);
        }
 
-       /* Remove the client entry */
-       silc_server_remove_clients_channels(server, NULL, client, channels);
-       client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
-       id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       /* Update statistics */
        server->stat.clients--;
        if (server->server_type == SILC_ROUTER)
          server->stat.cell_clients--;
+       SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+       SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+       /* Remove the client entry */
+       silc_server_remove_clients_channels(server, NULL, client, channels);
+       if (!server_signoff) {
+         client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+         id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+       } else {
+         silc_idlist_del_client(server->global_list, client);
+       }
 
        if (!silc_idcache_list_next(list, &id_cache))
          break;
@@ -323,9 +343,12 @@ bool silc_server_remove_clients_by_server(SilcServer server,
      this server's client(s) on the channel. As they left the channel we
      must re-generate the channel key. */
   silc_hash_table_list(channels, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
-    if (!silc_server_create_channel_key(server, channel, 0))
+  while (silc_hash_table_get(&htl, NULL, (void **)&channel)) {
+    if (!silc_server_create_channel_key(server, channel, 0)) {
+      silc_hash_table_list_reset(&htl);
+      silc_hash_table_free(channels);
       return FALSE;
+    }
 
     /* Do not send the channel key if private channel key mode is set */
     if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
@@ -335,6 +358,7 @@ bool silc_server_remove_clients_by_server(SilcServer server,
                                 server->server_type == SILC_ROUTER ? 
                                 FALSE : !server->standalone);
   }
+  silc_hash_table_list_reset(&htl);
   silc_hash_table_free(channels);
 
   return TRUE;
@@ -371,7 +395,7 @@ silc_server_update_clients_by_real_server(SilcServer server,
            SILC_LOG_DEBUG(("Moving client to local list"));
            silc_idcache_add(server->local_list->clients, client_cache->name,
                             client_cache->id, client_cache->context,
-                            client_cache->expire);
+                            client_cache->expire, NULL);
            silc_idcache_del_by_context(server->global_list->clients, client);
          }
          server_entry = server_entry->router;
@@ -382,7 +406,7 @@ silc_server_update_clients_by_real_server(SilcServer server,
            SILC_LOG_DEBUG(("Moving client to local list"));
            silc_idcache_add(server->local_list->clients, client_cache->name,
                             client_cache->id, client_cache->context,
-                            client_cache->expire);
+                            client_cache->expire, NULL);
            silc_idcache_del_by_context(server->global_list->clients, client);
          }
        }
@@ -418,7 +442,7 @@ silc_server_update_clients_by_real_server(SilcServer server,
            SILC_LOG_DEBUG(("Moving client to global list"));
            silc_idcache_add(server->global_list->clients, client_cache->name,
                             client_cache->id, client_cache->context,
-                            client_cache->expire);
+                            client_cache->expire, NULL);
            silc_idcache_del_by_context(server->local_list->clients, client);
          }
          server_entry = server_entry->router;
@@ -429,7 +453,7 @@ silc_server_update_clients_by_real_server(SilcServer server,
            SILC_LOG_DEBUG(("Moving client to global list"));
            silc_idcache_add(server->global_list->clients, client_cache->name,
                             client_cache->id, client_cache->context,
-                            client_cache->expire);
+                            client_cache->expire, NULL);
            silc_idcache_del_by_context(server->local_list->clients, client);
          }
        }
@@ -682,10 +706,13 @@ bool silc_server_channel_has_global(SilcChannelEntry channel)
   SilcHashTableList htl;
 
   silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (chl->client->router)
+  while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+    if (chl->client->router) {
+      silc_hash_table_list_reset(&htl);
       return TRUE;
+    }
   }
+  silc_hash_table_list_reset(&htl);
 
   return FALSE;
 }
@@ -699,10 +726,13 @@ bool silc_server_channel_has_local(SilcChannelEntry channel)
   SilcHashTableList htl;
 
   silc_hash_table_list(channel->user_list, &htl);
-  while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
-    if (!chl->client->router)
+  while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+    if (!chl->client->router) {
+      silc_hash_table_list_reset(&htl);
       return TRUE;
+    }
   }
+  silc_hash_table_list_reset(&htl);
 
   return FALSE;
 }
@@ -718,8 +748,111 @@ bool silc_server_client_on_channel(SilcClientEntry client,
   if (!client || !channel)
     return FALSE;
 
-  if (silc_hash_table_find(client->channels, channel, NULL, NULL))
-    return TRUE;
+  return silc_hash_table_find(client->channels, channel, NULL, NULL);
+}
+
+/* Checks string for bad characters and returns TRUE if they are found. */
+
+bool silc_server_name_bad_chars(const char *name, SilcUInt32 name_len)
+{
+  int i;
+
+  for (i = 0; i < name_len; i++) {
+    if (!isascii(name[i]))
+      return TRUE;
+    if (name[i] <= 32) return TRUE;
+    if (name[i] == ' ') return TRUE;
+    if (name[i] == '*') return TRUE;
+    if (name[i] == '?') return TRUE;
+    if (name[i] == ',') return TRUE;
+  }
 
   return FALSE;
 }
+
+/* Modifies the `name' if it includes bad characters and returns new
+   allocated name that does not include bad characters. */
+
+char *silc_server_name_modify_bad(const char *name, SilcUInt32 name_len)
+{
+  int i;
+  char *newname = strdup(name);
+
+  for (i = 0; i < name_len; i++) {
+    if (!isascii(newname[i])) newname[i] = '_';
+    if (newname[i] <= 32) newname[i] = '_';
+    if (newname[i] == ' ') newname[i] = '_';
+    if (newname[i] == '*') newname[i] = '_';
+    if (newname[i] == '?') newname[i] = '_';
+    if (newname[i] == ',') newname[i] = '_';
+  }
+
+  return newname;
+}
+
+/* Find number of sockets by IP address indicated by `ip'. Returns 0 if
+   socket connections with the IP address does not exist. */
+
+SilcUInt32 silc_server_num_sockets_by_ip(SilcServer server, const char *ip,
+                                        SilcSocketType type)
+{
+  int i, count;
+
+  for (i = 0, count = 0; i < server->config->param.connections_max; i++) {
+    if (server->sockets[i] && !strcmp(server->sockets[i]->ip, ip) &&
+       server->sockets[i]->type == type)
+      count++;
+  }
+
+  return count;
+}
+
+/* Finds locally cached public key by the public key received in the SKE. 
+   If we have it locally cached then we trust it and will use it in the
+   authentication protocol.  Returns the locally cached public key or NULL
+   if we do not find the public key.  */
+
+SilcPublicKey silc_server_find_public_key(SilcServer server, 
+                                         SilcHashTable local_public_keys,
+                                         SilcPublicKey remote_public_key)
+{
+  SilcPublicKey cached_key;
+
+  SILC_LOG_DEBUG(("Find remote public key (%d keys in local cache)",
+                 silc_hash_table_count(local_public_keys)));
+
+  if (!silc_hash_table_find_ext(local_public_keys, remote_public_key,
+                               (void **)&cached_key, NULL, 
+                               silc_hash_public_key, NULL,
+                               silc_hash_public_key_compare, NULL)) {
+    SILC_LOG_ERROR(("Public key not found"));
+    return NULL;
+  }
+
+  SILC_LOG_DEBUG(("Found public key"));
+
+  return cached_key;
+}
+
+/* This returns the first public key from the table of public keys.  This
+   is used only in cases where single public key exists in the table and
+   we want to get a pointer to it.  For public key tables that has multiple
+   keys in it the silc_server_find_public_key must be used. */
+
+SilcPublicKey silc_server_get_public_key(SilcServer server,
+                                        SilcHashTable local_public_keys)
+{
+  SilcPublicKey cached_key;
+  SilcHashTableList htl;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  assert(silc_hash_table_count(local_public_keys) < 2);
+
+  silc_hash_table_list(local_public_keys, &htl);
+  if (!silc_hash_table_get(&htl, NULL, (void **)&cached_key))
+    return NULL;
+  silc_hash_table_list_reset(&htl);
+
+  return cached_key;
+}