silcd: check entity validity after command reply
[silc.git] / apps / silcd / command.c
index f35969ecf8968c3377595a04bd8fe7b84e63c50e..9439f488ceaa2878cb86f9f517ab91c406d7edf3 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2008 Pekka Riikonen
+  Copyright (C) 1997 - 2009 Pekka Riikonen
 
   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
@@ -16,7 +16,6 @@
   GNU General Public License for more details.
 
 */
-/* $Id$ */
 
 #include "serverincludes.h"
 #include "server_internal.h"
@@ -86,6 +85,27 @@ SilcServerCommand silc_command_list[] =
   { NULL, 0 },
 };
 
+/* Returns TRUE if the connection is registered. Unregistered connections
+   usually cannot send commands hence the check. */
+
+static int silc_server_is_registered(SilcServer server,
+                                    SilcPacketStream sock,
+                                    SilcServerCommandContext cmd,
+                                    SilcCommand command)
+{
+  SilcIDListData idata = silc_packet_get_context(sock);
+
+  if (!idata)
+    return FALSE;
+
+  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
+    return TRUE;
+
+  silc_server_command_send_status_reply(cmd, command,
+                                       SILC_STATUS_ERR_NOT_REGISTERED, 0);
+  return FALSE;
+}
+
 /* Performs several checks to the command. It first checks whether this
    command was called as pending command callback. If it was then it checks
    whether error occurred in the command reply where the pending command
@@ -103,6 +123,13 @@ do {                                                                            \
     return;                                                                 \
   }                                                                         \
                                                                             \
+  if (context2 &&                                                           \
+      !silc_server_is_registered(cmd->server, cmd->sock, cmd, command)) {    \
+    SILC_LOG_DEBUG(("Not registered, command not called"));                 \
+    silc_server_command_free(cmd);                                          \
+    return;                                                                 \
+  }                                                                         \
+                                                                            \
   _argc = silc_argument_get_arg_num(cmd->args);                                     \
   if (_argc < min) {                                                        \
     SILC_LOG_DEBUG(("Not enough parameters in command"));                   \
@@ -122,27 +149,6 @@ do {                                                                            \
   }                                                                         \
 } while(0)
 
-/* Returns TRUE if the connection is registered. Unregistered connections
-   usually cannot send commands hence the check. */
-
-static int silc_server_is_registered(SilcServer server,
-                                    SilcPacketStream sock,
-                                    SilcServerCommandContext cmd,
-                                    SilcCommand command)
-{
-  SilcIDListData idata = silc_packet_get_context(sock);
-
-  if (!idata)
-    return FALSE;
-
-  if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
-    return TRUE;
-
-  silc_server_command_send_status_reply(cmd, command,
-                                       SILC_STATUS_ERR_NOT_REGISTERED, 0);
-  return FALSE;
-}
-
 /* Internal context to hold data when executed command with timeout. */
 typedef struct {
   SilcServerCommandContext ctx;
@@ -339,7 +345,7 @@ SILC_TASK_CALLBACK(silc_server_command_pending_timeout)
   SilcBuffer tmpreply;
   int i;
 
-  SILC_LOG_DEBUG(("Timeout pending command"));
+  SILC_LOG_DEBUG(("Timeout pending command %p", reply));
 
   /* Allocate temporary and bogus command reply context */
   cmdr = silc_calloc(1, sizeof(*cmdr));
@@ -1472,12 +1478,19 @@ SILC_SERVER_CMD_FUNC(kill)
 
     /* Do normal signoff for the destination client */
     sock = remote_client->connection;
+
+    if (sock)
+      silc_packet_stream_ref(sock);
+
     silc_server_remove_from_channels(server, NULL, remote_client,
                                     TRUE, (char *)"Killed", TRUE, TRUE);
     silc_server_free_sock_user_data(server, sock, comment ? comment :
                                    (unsigned char *)"Killed");
-    if (sock)
+    if (sock) {
+      silc_packet_set_context(sock, NULL);
       silc_server_close_connection(server, sock);
+      silc_packet_stream_unref(sock);
+    }
   } else {
     /* Router operator killing */
 
@@ -2372,7 +2385,7 @@ SILC_SERVER_CMD_FUNC(join)
        channel will be global, based on our router name. */
     if (!serv[0] && !server->config->local_channels) {
       if (!server->standalone) {
-       silc_snprintf(serv, sizeof(serv), server->router->server_name);
+       silc_snprintf(serv, sizeof(serv), "%s", server->router->server_name);
       } else {
        SilcServerConfigRouter *router;
        router = silc_server_config_get_primary_router(server);
@@ -4058,22 +4071,47 @@ SILC_TASK_CALLBACK(silc_server_command_detach_cb)
 {
   SilcServer server = app_context;
   QuitInternal q = (QuitInternal)context;
-  SilcClientID *client_id = (SilcClientID *)q->sock;
-  SilcClientEntry client;
-  SilcPacketStream sock;
+  SilcPacketStream sock = q->sock;
+  SilcClientEntry client = silc_packet_get_context(sock);
+  SilcIDListData idata = (SilcIDListData)client;
 
-  client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                        TRUE, NULL);
-  if (client && client->connection) {
-    sock = client->connection;
+  if (!client) {
+    silc_packet_stream_unref(sock);
+    silc_free(q);
+    return;
+  }
 
-    /* Close the connection on our side */
-    client->router = NULL;
-    client->connection = NULL;
-    silc_server_close_connection(server, sock);
+  SILC_LOG_DEBUG(("Detaching client %s",
+                 silc_id_render(client->id, SILC_ID_CLIENT)));
+
+  /* Stop rekey for the client. */
+  silc_server_stop_rekey(server, client);
+
+  /* Abort any active protocol */
+  idata = silc_packet_get_context(sock);
+  if (idata && idata->sconn && idata->sconn->op) {
+    SILC_LOG_DEBUG(("Abort active protocol"));
+    silc_async_abort(idata->sconn->op, NULL, NULL);
+    idata->sconn->op = NULL;
   }
 
-  silc_free(client_id);
+  silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_heartbeat,
+                               sock);
+
+  /* Close the connection on our side */
+  client->router = NULL;
+  client->connection = NULL;
+  silc_server_close_connection(server, sock);
+
+  /* Mark the client as locally detached. */
+  client->local_detached = TRUE;
+
+  /* Decrement the user count; we'll increment it if the user resumes on our
+     server. */
+  SILC_VERIFY(&server->stat.my_clients > 0);
+  server->stat.my_clients--;
+
+  silc_packet_stream_unref(sock);
   silc_free(q);
 }
 
@@ -4140,14 +4178,15 @@ SILC_SERVER_CMD_FUNC(detach)
                                   SILC_NOTIFY_TYPE_UMODE_CHANGE);
 
   q = silc_calloc(1, sizeof(*q));
-  q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
+  q->sock = cmd->sock;
+  silc_packet_stream_ref(q->sock);
   silc_schedule_task_add_timeout(server->schedule,
                                 silc_server_command_detach_cb,
                                 q, 0, 200000);
 
   if (server->config->detach_timeout) {
     q = silc_calloc(1, sizeof(*q));
-    q->sock = silc_id_dup(client->id, SILC_ID_CLIENT);
+    q->sock = (void *)silc_id_dup(client->id, SILC_ID_CLIENT);
     silc_schedule_task_add_timeout(server->schedule,
                                   silc_server_command_detach_timeout,
                                   q, server->config->detach_timeout * 60, 0);