Print error about unknown channel ekys only on router since it's
[silc.git] / apps / silcd / server.c
index c568368b56f44dc81e1781cf8b61835b9cbbd104..f5e6b7fcffb001cc904539011cb935ff6cf5f77f 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  server.c
+  server.c 
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
@@ -8,8 +8,7 @@
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
+  the Free Software Foundation; version 2 of the License.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -78,6 +77,9 @@ int silc_server_alloc(SilcServer *new_server)
 
 void silc_server_free(SilcServer server)
 {
+  SilcIDCacheList list;
+  SilcIDCacheEntry cache;
+
   if (!server)
     return;
 
@@ -93,6 +95,7 @@ void silc_server_free(SilcServer server)
   }
 #endif
 
+  silc_server_backup_free(server);
   silc_server_config_unref(&server->config_ref);
   if (server->rng)
     silc_rng_free(server->rng);
@@ -107,6 +110,66 @@ void silc_server_free(SilcServer server)
   if (server->id_entry)
     silc_idlist_del_server(server->local_list, server->id_entry);
 
+  /* Delete all channels */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->channels, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_channel(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_channel(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->channels, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_channel(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_channel(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
+  /* Delete all clients */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->clients, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_client(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_client(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->clients, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_client(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_client(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
+  /* Delete all servers */
+  list = NULL;
+  if (silc_idcache_get_all(server->local_list->servers, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_server(server->local_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_server(server->local_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+  list = NULL;
+  if (silc_idcache_get_all(server->global_list->servers, &list) &&
+      silc_idcache_list_first(list, &cache)) {
+    silc_idlist_del_server(server->global_list, cache->context);
+    while (silc_idcache_list_next(list, &cache))
+      silc_idlist_del_server(server->global_list, cache->context);
+  }
+  if (list)
+    silc_idcache_list_free(list);
+
   silc_idcache_free(server->local_list->clients);
   silc_idcache_free(server->local_list->servers);
   silc_idcache_free(server->local_list->channels);
@@ -459,7 +522,7 @@ SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
   if (!sock)
     return;
 
-  SILC_LOG_INFO(("Closing connection %s:%d [%s]: connection is unconfigured",
+  SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
                 sock->hostname, sock->port,
                 (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                  sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
@@ -663,17 +726,20 @@ void silc_server_run(SilcServer server)
 
 void silc_server_stop(SilcServer server)
 {
-  SILC_LOG_DEBUG(("Stopping server"));
+  SILC_LOG_INFO(("SILC Server shutting down"));
 
   if (server->schedule) {
     int i;
 
+    server->server_shutdown = TRUE;
+
     /* Close all connections */
     for (i = 0; i < server->config->param.connections_max; i++) {
       if (!server->sockets[i])
        continue;
       if (!SILC_IS_LISTENER(server->sockets[i])) {
-       SilcIDListData idata = server->sockets[i]->user_data;
+       SilcSocketConnection sock = server->sockets[i];
+       SilcIDListData idata = sock->user_data;
 
        if (idata)
          idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
@@ -683,6 +749,10 @@ void silc_server_stop(SilcServer server)
        silc_server_disconnect_remote(server, server->sockets[i], 
                                      SILC_STATUS_OK, 
                                      "Server is shutting down");
+       if (sock->user_data)
+         silc_server_free_sock_user_data(server, sock,
+                                         "Server is shutting down");
+       silc_socket_free(sock);
       } else {
        silc_socket_free(server->sockets[i]);
        server->sockets[i] = NULL;
@@ -796,6 +866,15 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
   SilcServerConfigConnParams *param =
                (conn->param ? conn->param : &server->config->param);
 
+  /* Don't retry if we are shutting down. */
+  if (server->server_shutdown) {
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+
   SILC_LOG_INFO(("Retrying connecting to a router"));
 
   /* Calculate next timeout */
@@ -842,6 +921,14 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   SilcServerConfigRouter *rconn;
   int sock;
 
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown) {
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
+    return;
+  }
+
   SILC_LOG_INFO(("Connecting to the %s %s on port %d",
                 (sconn->backup ? "backup router" : "router"),
                 sconn->remote_host, sconn->remote_port));
@@ -893,6 +980,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
   SilcServerConnection sconn;
   SilcServerConfigRouter *ptr;
 
+  /* Don't connect if we are shutting down. */
+  if (server->server_shutdown)
+    return;
+
   SILC_LOG_DEBUG(("We are %s",
                  (server->server_type == SILC_SERVER ?
                   "normal server" : server->server_type == SILC_ROUTER ?
@@ -921,7 +1012,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
                    ptr->host, ptr->port));
 
     if (server->server_type == SILC_ROUTER && ptr->backup_router &&
-       ptr->initiator == FALSE && !server->backup_router)
+       ptr->initiator == FALSE && !server->backup_router &&
+       !silc_server_config_get_backup_router(server))
       server->wait_backup = TRUE;
 
     if (ptr->initiator) {
@@ -1277,6 +1369,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   if (!sconn->backup) {
     /* Mark this router our primary router if we're still standalone */
     if (server->standalone) {
+      SILC_LOG_DEBUG(("This connection is our primary router"));
       server->id_entry->router = id_entry;
       server->router = id_entry;
       server->standalone = FALSE;
@@ -1308,7 +1401,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
  out:
   /* Call the completion callback to indicate that we've connected to
      the router */
-  if (sconn->callback)
+  if (sconn && sconn->callback)
     (*sconn->callback)(server, id_entry, sconn->callback_context);
 
   /* Free the temporary connection data context */
@@ -1636,7 +1729,9 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
                           SILC_TASK_PRI_LOW);
 }
 
-/* After this is called, server don't wait for backup router anymore */
+/* After this is called, server don't wait for backup router anymore.  
+   This gets called automatically even after we have backup router
+   connection established. */
 
 SILC_TASK_CALLBACK(silc_server_backup_router_wait)
 {
@@ -1757,6 +1852,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       if (conn->param) {
        if (conn->param->keepalive_secs)
          hearbeat_timeout = conn->param->keepalive_secs;
+
+       /* Check if to be anonymous connection */
+       if (conn->param->anonymous)
+         client->mode |= SILC_UMODE_ANONYMOUS;
       }
 
       id_entry = (void *)client;
@@ -1907,15 +2006,16 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       /* If the incoming connection is router and marked as backup router
         then add it to be one of our backups */
       if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && backup_router) {
-       silc_server_backup_add(server, new_server, backup_replace_ip,
-                              backup_replace_port, backup_local);
-
        /* Change it back to SERVER type since that's what it really is. */
        if (backup_local)
          ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
-
        new_server->server_type = SILC_BACKUP_ROUTER;
-       server->wait_backup = FALSE;
+
+       /* Remove the backup waiting with timeout */
+       silc_schedule_task_add(server->schedule, 0,
+                              silc_server_backup_router_wait,
+                              (void *)server, 5, 0,
+                              SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
       }
 
       /* Statistics */
@@ -2149,8 +2249,9 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real)
     ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
 
   /* If entry is disabled ignore what we got. */
-  if (ret != SILC_PACKET_RESUME_ROUTER &&
-      idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
+  if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED && 
+      ret != SILC_PACKET_HEARTBEAT && ret != SILC_PACKET_RESUME_ROUTER &&
+      ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE) {
     SILC_LOG_DEBUG(("Connection is disabled"));
     goto out;
   }
@@ -2751,6 +2852,8 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final)
 void silc_server_close_connection(SilcServer server,
                                  SilcSocketConnection sock)
 {
+  char tmp[128];
+
   if (!server->sockets[sock->sock] && SILC_IS_DISCONNECTED(sock)) {
     silc_schedule_task_add(server->schedule, 0,
                           silc_server_close_connection_final,
@@ -2759,12 +2862,14 @@ void silc_server_close_connection(SilcServer server,
     return;
   }
 
-  SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
+  memset(tmp, 0, sizeof(tmp));
+  silc_socket_get_error(sock, tmp, sizeof(tmp));
+  SILC_LOG_INFO(("Closing connection %s:%d [%s] %s", sock->hostname,
                   sock->port,
                   (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                    sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
                    sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
-                   "Router")));
+                   "Router"), tmp[0] ? tmp : ""));
 
   /* We won't listen for this connection anymore */
   silc_schedule_unset_listen_fd(server->schedule, sock->sock);
@@ -2861,6 +2966,8 @@ SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
 {
   FreeClientInternal i = (FreeClientInternal)context;
 
+  assert(!silc_hash_table_count(i->client->channels));
+
   silc_idlist_del_data(i->client);
   silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
   silc_free(i);
@@ -2874,8 +2981,6 @@ void silc_server_free_client_data(SilcServer server,
                                  int notify,
                                  const char *signoff)
 {
-  FreeClientInternal i = silc_calloc(1, sizeof(*i));
-
   SILC_LOG_DEBUG(("Freeing client data"));
 
 #if 1
@@ -2926,17 +3031,26 @@ void silc_server_free_client_data(SilcServer server,
   silc_schedule_task_del_by_context(server->schedule, client);
 
   /* We will not delete the client entry right away. We will take it
-     into history (for WHOWAS command) for 5 minutes */
-  i->server = server;
-  i->client = client;
-  silc_schedule_task_add(server->schedule, 0,
-                        silc_server_free_client_data_timeout,
-                        (void *)i, 300, 0,
-                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-  client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
-  client->mode = 0;
-  client->router = NULL;
-  client->connection = NULL;
+     into history (for WHOWAS command) for 5 minutes, unless we're
+     shutting down server. */
+  if (!server->server_shutdown) {
+    FreeClientInternal i = silc_calloc(1, sizeof(*i));
+    i->server = server;
+    i->client = client;
+    silc_schedule_task_add(server->schedule, 0,
+                          silc_server_free_client_data_timeout,
+                          (void *)i, 300, 0,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+    client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+    client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
+    client->mode = 0;
+    client->router = NULL;
+    client->connection = NULL;
+  } else {
+    /* Delete directly since we're shutting down server */
+    silc_idlist_del_data(client);
+    silc_idlist_del_client(server->local_list, client);
+  }
 }
 
 /* Frees user_data pointer from socket connection object. This also sends
@@ -2966,6 +3080,14 @@ void silc_server_free_sock_user_data(SilcServer server,
       if (user_data->id)
        backup_router = silc_server_backup_get(server, user_data->id);
 
+      if (!server->backup_router && server->server_type == SILC_ROUTER &&
+         backup_router == server->id_entry &&
+         sock->type != SILC_SOCKET_TYPE_ROUTER)
+       backup_router = NULL;
+
+      if (server->server_shutdown)
+       backup_router = NULL;
+
       /* If this was our primary router connection then we're lost to
         the outside world. */
       if (server->router == user_data) {
@@ -3030,9 +3152,16 @@ void silc_server_free_sock_user_data(SilcServer server,
        silc_server_remove_servers_by_server(server, user_data, TRUE);
 
        /* Remove the clients that this server owns as they will become
-          invalid now too. */
-       silc_server_remove_clients_by_server(server, user_data,
-                                            user_data, TRUE);
+          invalid now too.  For backup router the server is actually
+          coming from the primary router, so mark that as the owner
+          of this entry. */
+       if (server->server_type == SILC_BACKUP_ROUTER &&
+           sock->type == SILC_SOCKET_TYPE_SERVER)
+         silc_server_remove_clients_by_server(server, server->router,
+                                              user_data, TRUE);
+       else
+         silc_server_remove_clients_by_server(server, user_data,
+                                              user_data, TRUE);
 
        /* Remove channels owned by this server */
        if (server->server_type == SILC_SERVER)
@@ -3074,7 +3203,7 @@ void silc_server_free_sock_user_data(SilcServer server,
                                 ("%s switched to backup router %s "
                                  "(we are primary router now)",
                                  server->server_name, server->server_name));
-       else
+       else if (server->router)
          SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
                                 SILC_NOTIFY_TYPE_NONE,
                                 ("%s switched to backup router %s",
@@ -3175,7 +3304,7 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* Remove channel if this is last client leaving the channel, unless
        the channel is permanent. */
-    if (server->server_type == SILC_ROUTER &&
+    if (server->server_type != SILC_SERVER &&
        silc_hash_table_count(channel->user_list) < 2) {
       silc_server_channel_delete(server, channel);
       continue;
@@ -3187,7 +3316,7 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* 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. */
-    if (server->server_type != SILC_ROUTER && channel->global_users &&
+    if (server->server_type == SILC_SERVER && channel->global_users &&
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
@@ -3204,7 +3333,7 @@ void silc_server_remove_from_channels(SilcServer server,
     /* If there is not at least one local user on the channel then we don't
        need the channel entry anymore, we can remove it safely, unless the
        channel is permanent channel */
-    if (server->server_type != SILC_ROUTER &&
+    if (server->server_type == SILC_SERVER &&
        !silc_server_channel_has_local(channel)) {
       /* Notify about leaving client if this channel has global users. */
       if (notify && channel->global_users)
@@ -3229,6 +3358,10 @@ void silc_server_remove_from_channels(SilcServer server,
                                         signoff_message, signoff_message ?
                                         strlen(signoff_message) : 0);
 
+    /* Don't create keys if we are shutting down */
+    if (server->server_shutdown)
+      continue;
+
     /* Re-generate channel key if needed */
     if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
       if (!silc_server_create_channel_key(server, channel, 0))
@@ -3273,7 +3406,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
 
   /* Remove channel if this is last client leaving the channel, unless
      the channel is permanent. */
-  if (server->server_type == SILC_ROUTER &&
+  if (server->server_type != SILC_SERVER &&
       silc_hash_table_count(channel->user_list) < 2) {
     silc_server_channel_delete(server, channel);
     return FALSE;
@@ -3285,7 +3418,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
 
   /* If there is no global users on the channel anymore mark the channel
      as local channel. Do not check if the client is local client. */
-  if (server->server_type != SILC_ROUTER && channel->global_users &&
+  if (server->server_type == SILC_SERVER && channel->global_users &&
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
@@ -3306,7 +3439,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
   /* If there is not at least one local user on the channel then we don't
      need the channel entry anymore, we can remove it safely, unless the
      channel is permanent channel */
-  if (server->server_type != SILC_ROUTER &&
+  if (server->server_type == SILC_SERVER &&
       !silc_server_channel_has_local(channel)) {
     /* Notify about leaving client if this channel has global users. */
     if (notify && channel->global_users)
@@ -3565,6 +3698,10 @@ SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
 
   rekey->task = NULL;
 
+  /* Return now if we are shutting down */
+  if (server->server_shutdown)
+    return;
+
   if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len))
     return;
 
@@ -3687,8 +3824,9 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
     if (!channel) {
       channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
       if (!channel) {
-       SILC_LOG_ERROR(("Received key for non-existent channel %s",
-                       silc_id_render(id, SILC_ID_CHANNEL)));
+       if (server->server_type == SILC_ROUTER)
+         SILC_LOG_ERROR(("Received key for non-existent channel %s",
+                         silc_id_render(id, SILC_ID_CHANNEL)));
        goto out;
       }
     }
@@ -3775,7 +3913,8 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock,
 {
   SilcServerHBContext hb = (SilcServerHBContext)hb_context;
 
-  SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
+  SILC_LOG_DEBUG(("Sending heartbeat to %s:%d (%s)", sock->hostname, 
+                sock->port, sock->ip));
 
   /* Send the heartbeat */
   silc_server_send_heartbeat(hb->server, sock);
@@ -3908,6 +4047,12 @@ static void silc_server_announce_get_clients(SilcServer server,
            break;
          continue;
        }
+       if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) &&
+           !client->connection && !client->router && !SILC_IS_LOCAL(client)) {
+         if (!silc_idcache_list_next(list, &id_cache))
+           break;
+         continue;
+       }
 
        idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
 
@@ -4444,9 +4589,9 @@ void silc_server_save_users_on_channel(SilcServer server,
   SilcClientEntry client;
   SilcIDCacheEntry cache;
   SilcChannelClientEntry chl;
-  bool global;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Saving %d users on %s channel", user_count,
+                 channel->channel_name));
 
   for (i = 0; i < user_count; i++) {
     /* Client ID */
@@ -4466,21 +4611,19 @@ void silc_server_save_users_on_channel(SilcServer server,
       continue;
     }
 
-    global = FALSE;
+    cache = NULL;
 
     /* Check if we have this client cached already. */
     client = silc_idlist_find_client_by_id(server->local_list, client_id,
                                           server->server_type, &cache);
-    if (!client) {
+    if (!client)
       client = silc_idlist_find_client_by_id(server->global_list,
                                             client_id, server->server_type,
                                             &cache);
-      global = TRUE;
-    }
     if (!client) {
       /* If router did not find such Client ID in its lists then this must
         be bogus client or some router in the net is buggy. */
-      if (server->server_type == SILC_ROUTER) {
+      if (server->server_type != SILC_SERVER) {
        silc_free(client_id);
        continue;
       }
@@ -4498,15 +4641,18 @@ void silc_server_save_users_on_channel(SilcServer server,
       }
 
       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
-    } else {
-      /* Found, if it is from global list we'll assure that we won't
-        expire it now that the entry is on channel. */
-      if (global)
-       cache->expire = 0;
     }
 
+    if (cache)
+      cache->expire = 0;
     silc_free(client_id);
 
+    if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+      SILC_LOG_ERROR(("Attempting to add unregistered client to channel ",
+                     "%s", channel->channel_name));
+      continue;
+    }
+
     if (!silc_server_client_on_channel(client, channel, &chl)) {
       /* Client was not on the channel, add it. */
       chl = silc_calloc(1, sizeof(*chl));
@@ -4544,7 +4690,8 @@ void silc_server_save_user_channels(SilcServer server,
   char *name;
   int i = 0;
 
-  if (!channels ||!channels_user_modes)
+  if (!channels || !channels_user_modes ||
+      !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
     goto out;
   
   ch = silc_channel_payload_parse_list(channels->data, channels->len);