Send disconnect packet before freeing socket data in
[silc.git] / apps / silcd / server.c
index d75fdd803526407382be26878b938fb64ef393cf..7ee66b1fe8388b342a2b893db9ed7924b21bacfb 100644 (file)
@@ -201,7 +201,7 @@ static bool silc_server_listen(SilcServer server, const char *server_ip,
 {
   *sock = silc_net_create_server(port, server_ip);
   if (*sock < 0) {
-    SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
+    SILC_SERVER_LOG_ERROR(("Could not create server listener: %s on %hu",
                        server_ip, port));
     return FALSE;
   }
@@ -252,10 +252,10 @@ bool silc_server_init_secondary(SilcServer server)
 
     newsocket->user_data = (void *)server->id_entry;
     silc_schedule_task_add(server->schedule, sock_list[sock],
-                        silc_server_accept_new_connection,
-                        (void *)server, 0, 0,
-                        SILC_TASK_FD,
-                        SILC_TASK_PRI_NORMAL);
+                          silc_server_accept_new_connection,
+                          (void *)server, 0, 0,
+                          SILC_TASK_FD,
+                          SILC_TASK_PRI_NORMAL);
   }
 
   return TRUE;
@@ -285,7 +285,7 @@ bool silc_server_init(SilcServer server)
   server->starttime = time(NULL);
 
   /* Take config object for us */
-  silc_server_config_ref(&server->config_ref, server->config, 
+  silc_server_config_ref(&server->config_ref, server->config,
                         server->config);
 
   /* Steal public and private key from the config object */
@@ -343,7 +343,7 @@ bool silc_server_init(SilcServer server)
   server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
 
   /* Init watcher list */
-  server->watcher_list = 
+  server->watcher_list =
     silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
                          silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
                          NULL, NULL, TRUE);
@@ -1411,7 +1411,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
       /* If we are backup router then this primary router is whom we are
         backing up. */
       if (server->server_type == SILC_BACKUP_ROUTER)
-       silc_server_backup_add(server, server->id_entry, sock->ip, 0, TRUE);
+       silc_server_backup_add(server, server->id_entry, sock->ip,
+                              sconn->remote_port, TRUE);
     }
   } else {
     /* Add this server to be our backup router */
@@ -1469,8 +1470,6 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   SilcServerConfigDeny *deny;
   int port;
 
-  context = (void *)server;
-
   /* Check whether we could resolve both IP and FQDN. */
   if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
                    server->config->require_reverse_lookup)) {
@@ -1491,6 +1490,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
      However, this doesn't set the scheduler for outgoing traffic, it
      will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
      later when outgoing data is available. */
+  context = (void *)server;
   SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
 
   SILC_LOG_INFO(("Incoming connection %s (%s)", sock->hostname,
@@ -2234,14 +2234,13 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
                                    TRUE : FALSE, cipher, hmac, sequence,
                                    silc_server_packet_parse, server);
 
-  /* If this socket connection is not authenticated yet and the packet
-     processing failed we will drop the connection since it can be
-     a malicious flooder. */
-  if (sock->type == SILC_SOCKET_TYPE_UNKNOWN && ret == FALSE &&
-      (!sock->protocol || sock->protocol->protocol->type ==
-       SILC_PROTOCOL_SERVER_KEY_EXCHANGE)) {
-    SILC_LOG_DEBUG(("Bad data sent from unknown connection %d", sock->sock));
-    SILC_SET_DISCONNECTING(sock);
+  /* If processing failed the connection is closed. */
+  if (!ret) {
+    /* On packet processing errors we may close our primary router 
+       connection but won't become primary router if we are the backup
+       since this is local error condition. */
+    if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
+      server->backup_noswitch = TRUE;
 
     if (sock->user_data)
       silc_server_free_sock_user_data(server, sock, NULL);
@@ -2348,6 +2347,7 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
   SilcServer server = (SilcServer)context;
   SilcSocketConnection sock = parser_context->sock;
   SilcIDListData idata = (SilcIDListData)sock->user_data;
+  bool ret;
 
   if (idata)
     idata->psn_receive = parser_context->packet->sequence + 1;
@@ -2368,14 +2368,29 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context,
        the idata->receive_key might have become valid in the last packet
        and we want to call this processor with valid cipher. */
     if (idata)
-      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+      ret = silc_packet_receive_process(
+                                 sock, server->server_type == SILC_ROUTER ?
                                  TRUE : FALSE, idata->receive_key,
                                  idata->hmac_receive, idata->psn_receive,
                                  silc_server_packet_parse, server);
     else
-      silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ?
+      ret = silc_packet_receive_process(
+                                 sock, server->server_type == SILC_ROUTER ?
                                  TRUE : FALSE, NULL, NULL, 0,
                                  silc_server_packet_parse, server);
+
+    if (!ret) {
+      /* On packet processing errors we may close our primary router 
+         connection but won't become primary router if we are the backup
+         since this is local error condition. */
+      if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
+       server->backup_noswitch = TRUE;
+
+      if (sock->user_data)
+       silc_server_free_sock_user_data(server, sock, NULL);
+      silc_server_close_connection(server, sock);
+    }
+
     return FALSE;
   }
 
@@ -2934,7 +2949,7 @@ void silc_server_disconnect_remote(SilcServer server,
   char *cp;
   int len;
 
-  if (!sock)
+  if (!sock || SILC_IS_DISCONNECTED(sock))
     return;
 
   memset(buf, 0, sizeof(buf));
@@ -3085,7 +3100,7 @@ void silc_server_free_sock_user_data(SilcServer server,
          sock->type != SILC_SOCKET_TYPE_ROUTER)
        backup_router = NULL;
 
-      if (server->server_shutdown)
+      if (server->server_shutdown || server->backup_noswitch)
        backup_router = NULL;
 
       /* If this was our primary router connection then we're lost to
@@ -3144,6 +3159,14 @@ void silc_server_free_sock_user_data(SilcServer server,
        /* Mark this connection as replaced */
        silc_server_backup_replaced_add(server, user_data->id,
                                        backup_router);
+      } else if (server->server_type == SILC_SERVER &&
+                sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       /* Reconnect to the router (backup) */
+       silc_schedule_task_add(server->schedule, 0,
+                              silc_server_connect_to_router,
+                              server, 1, 0,
+                              SILC_TASK_TIMEOUT,
+                              SILC_TASK_PRI_NORMAL);
       }
 
       if (!backup_router) {
@@ -3210,6 +3233,7 @@ void silc_server_free_sock_user_data(SilcServer server,
                                  server->server_name,
                                  server->router->server_name));
       }
+      server->backup_noswitch = FALSE;
 
       /* Free the server entry */
       silc_server_backup_del(server, user_data);
@@ -3311,7 +3335,7 @@ void silc_server_remove_from_channels(SilcServer server,
     }
 
     silc_hash_table_del(client->channels, channel);
-    silc_hash_table_del(channel->user_list, chl->client);
+    silc_hash_table_del(channel->user_list, client);
     channel->user_count--;
 
     /* If there is no global users on the channel anymore mark the channel
@@ -3320,6 +3344,7 @@ void silc_server_remove_from_channels(SilcServer server,
        chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
+    memset(chl, 'A', sizeof(*chl));
     silc_free(chl);
 
     /* Update statistics */
@@ -3412,8 +3437,8 @@ bool silc_server_remove_from_one_channel(SilcServer server,
     return FALSE;
   }
 
-  silc_hash_table_del(client->channels, chl->channel);
-  silc_hash_table_del(channel->user_list, chl->client);
+  silc_hash_table_del(client->channels, channel);
+  silc_hash_table_del(channel->user_list, client);
   channel->user_count--;
 
   /* If there is no global users on the channel anymore mark the channel
@@ -3422,6 +3447,7 @@ bool silc_server_remove_from_one_channel(SilcServer server,
       chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
+  memset(chl, 'O', sizeof(*chl));
   silc_free(chl);
 
   /* Update statistics */
@@ -3492,15 +3518,15 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote)
     return;
   }
 
-  if (sock->user_data)
-    silc_server_free_sock_user_data(server, sock, NULL);
-
   silc_server_disconnect_remote(server, sock, 
                                protocol == 
                                SILC_PROTOCOL_SERVER_CONNECTION_AUTH ?
                                SILC_STATUS_ERR_AUTH_FAILED :
                                SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
                                "Connection timeout");
+
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock, NULL);
 }
 
 /* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
@@ -4936,61 +4962,6 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
   return buffer;
 }
 
-/* Finds client entry by Client ID and if it is not found then resolves
-   it using WHOIS command. */
-
-SilcClientEntry silc_server_get_client_resolve(SilcServer server,
-                                              SilcClientID *client_id,
-                                              bool always_resolve,
-                                              bool *resolved)
-{
-  SilcClientEntry client;
-
-  if (resolved)
-    *resolved = FALSE;
-
-  client = silc_idlist_find_client_by_id(server->local_list, client_id,
-                                        TRUE, NULL);
-  if (!client) {
-    client = silc_idlist_find_client_by_id(server->global_list,
-                                          client_id, TRUE, NULL);
-    if (!client && server->server_type == SILC_ROUTER)
-      return NULL;
-  }
-
-  if (!client && server->standalone)
-    return NULL;
-
-  if (!client || !client->nickname || !client->username ||
-      always_resolve) {
-    SilcBuffer buffer, idp;
-
-    if (client) {
-      client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
-      client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
-      client->resolve_cmd_ident = ++server->cmd_ident;
-    }
-
-    idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
-    buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
-                                           server->cmd_ident, 1,
-                                           4, idp->data, idp->len);
-    silc_server_packet_send(server, client ? client->router->connection :
-                           SILC_PRIMARY_ROUTE(server),
-                           SILC_PACKET_COMMAND, 0,
-                           buffer->data, buffer->len, FALSE);
-    silc_buffer_free(idp);
-    silc_buffer_free(buffer);
-
-    if (resolved)
-      *resolved = TRUE;
-
-    return NULL;
-  }
-
-  return client;
-}
-
 /* A timeout callback for the re-key. We will be the initiator of the
    re-key protocol. */