Static analyzer bug fixes
[silc.git] / apps / silcd / server.c
index 05deee4b61613878979e263954dff52bc3c73eee..4712bb2d05b60621f4ff13917ad6905028e1070a 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
@@ -53,7 +53,7 @@ silc_server_verify_key(SilcSKE ske,
 
   SILC_LOG_DEBUG(("Verifying public key"));
 
-  if (silc_pkcs_get_type(public_key) != SILC_SKE_PK_TYPE_SILC) {
+  if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) {
     SILC_LOG_WARNING(("We don't support %s (%s) port %d public key type %d",
                      entry->hostname, entry->ip, entry->port,
                      silc_pkcs_get_type(public_key)));
@@ -196,25 +196,23 @@ static void silc_server_packet_eos(SilcPacketEngine engine,
 
   SILC_LOG_DEBUG(("End of stream received, sock %p", stream));
 
-  if (!idata)
-    return;
-
   if (server->router_conn && server->router_conn->sock == stream &&
       !server->router && server->standalone) {
-    if (idata->sconn && idata->sconn->callback)
+    if (idata && idata->sconn && idata->sconn->callback)
       (*idata->sconn->callback)(server, NULL, idata->sconn->callback_context);
     silc_server_create_connections(server);
     silc_server_free_sock_user_data(server, stream, NULL);
   } else {
     /* If backup disconnected then mark that resuming will not be allowed */
-     if (server->server_type == SILC_ROUTER && !server->backup_router &&
+     if (idata &&
+        server->server_type == SILC_ROUTER && !server->backup_router &&
          idata->conn_type == SILC_CONN_SERVER) {
       SilcServerEntry server_entry = (SilcServerEntry)idata;
       if (server_entry->server_type == SILC_BACKUP_ROUTER)
         server->backup_closed = TRUE;
     }
 
-    if (idata->sconn && idata->sconn->callback)
+    if (idata && idata->sconn && idata->sconn->callback)
       (*idata->sconn->callback)(server, NULL, idata->sconn->callback_context);
     silc_server_free_sock_user_data(server, stream, NULL);
   }
@@ -228,16 +226,21 @@ SILC_TASK_CALLBACK(silc_server_packet_error_timeout)
   SilcPacketStream stream = context;
   SilcIDListData idata = silc_packet_get_context(stream);
 
-  if (!idata)
+  if (!idata || !silc_packet_stream_is_valid(stream)) {
+    silc_packet_stream_unref(stream);
     return;
+  }
 
   if (server->router_conn && server->router_conn->sock == stream &&
       !server->router && server->standalone) {
+    if (idata->sconn && idata->sconn->callback)
+      (*idata->sconn->callback)(server, NULL, idata->sconn->callback_context);
     silc_server_create_connections(server);
+    silc_server_free_sock_user_data(server, stream, NULL);
   } else {
     /* If backup disconnected then mark that resuming will not be allowed */
-     if (server->server_type == SILC_ROUTER && !server->backup_router &&
-         idata->conn_type == SILC_CONN_SERVER) {
+    if (server->server_type == SILC_ROUTER && !server->backup_router &&
+        idata->conn_type == SILC_CONN_SERVER) {
       SilcServerEntry server_entry = (SilcServerEntry)idata;
       if (server_entry->server_type == SILC_BACKUP_ROUTER)
         server->backup_closed = TRUE;
@@ -249,6 +252,9 @@ SILC_TASK_CALLBACK(silc_server_packet_error_timeout)
   }
 
   silc_server_close_connection(server, stream);
+
+  /* Release our stream reference */
+  silc_packet_stream_unref(stream);
 }
 
 /* Packet engine callback to indicate error */
@@ -265,7 +271,7 @@ static void silc_server_packet_error(SilcPacketEngine engine,
   const char *ip;
   SilcUInt16 port;
 
-  SILC_LOG_DEBUG(("Packet error, sock %p", stream));
+  SILC_LOG_DEBUG(("Packet error %d, sock %p", error, stream));
 
   if (!idata || !sock)
     return;
@@ -280,9 +286,17 @@ static void silc_server_packet_error(SilcPacketEngine engine,
   if (!silc_packet_stream_is_valid(stream))
     return;
 
+  /* We must take reference of the stream */
+  silc_packet_stream_ref(stream);
+
+  /* In case we get here many times, register only one timeout */
+  silc_schedule_task_del_by_all(server->schedule, 0,
+                               silc_server_packet_error_timeout, stream);
+
+  /* Close connection with random timeout */
   silc_schedule_task_add_timeout(server->schedule,
-                                silc_server_packet_error_timeout,
-                                stream, 0, 0);
+                                silc_server_packet_error_timeout, stream,
+                                silc_rng_get_byte(server->rng) % 10, 0);
 }
 
 /* Packet stream callbacks */
@@ -302,9 +316,16 @@ static void silc_server_packet_parse_type(SilcServer server,
 {
   SilcPacketType type = packet->type;
   SilcIDListData idata = silc_packet_get_context(sock);
+#ifdef SILC_DEBUG
+  const char *ip;
+  SilcUInt16 port;
 
-  SILC_LOG_DEBUG(("Received %s packet [flags %d]",
-                 silc_get_packet_name(type), packet->flags));
+  silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+                             NULL, NULL, &ip, &port);
+#endif /* SILC_DEBUG */
+
+  SILC_LOG_DEBUG(("Received %s packet [flags %d] from %s:%d",
+                 silc_get_packet_name(type), packet->flags, ip, port));
 
   /* Parse the packet type */
   switch (type) {
@@ -510,7 +531,7 @@ static void silc_server_packet_parse_type(SilcServer server,
 
   case SILC_PACKET_KEY_AGREEMENT:
     /*
-     * Received heartbeat.
+     * Received key agreement.
      */
     if (packet->flags & SILC_PACKET_FLAG_LIST)
       break;
@@ -767,7 +788,6 @@ SilcBool silc_server_init(SilcServer server)
   SilcNetListener listener;
   SilcUInt16 *port;
   char **ip;
-  char *external_ip;
 
   SILC_LOG_DEBUG(("Initializing server"));
 
@@ -871,11 +891,8 @@ SilcBool silc_server_init(SilcServer server)
   /* Create a Server ID for the server. */
   port = silc_net_listener_get_port(listener, NULL);
   ip = silc_net_listener_get_ip(listener, NULL);
-  external_ip = server->config->server_info->external_ip ?
-               server->config->server_info->external_ip :
-               server->config->server_info->primary->public_ip;
-  silc_id_create_server_id(external_ip ?
-                          external_ip :
+  silc_id_create_server_id(server->config->server_info->primary->public_ip ?
+                          server->config->server_info->primary->public_ip :
                           ip[0], port[0], server->rng, &id);
   if (!id)
     goto err;
@@ -1802,6 +1819,8 @@ void silc_server_start_key_exchange(SilcServerConnection sconn)
   }
   entry->server = server;
   entry->data.sconn = sconn;
+  entry->data.conn_type = SILC_CONN_UNKNOWN;
+  entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
   silc_packet_set_context(sconn->sock, entry);
 
   SILC_LOG_DEBUG(("Created unknown connection %p", entry));
@@ -2339,6 +2358,8 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
       entry->data.conn_type = SILC_CONN_CLIENT;
 
       /* Statistics */
+      SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                     server->stat.clients + 1));
       server->stat.my_clients++;
       server->stat.clients++;
       server->stat.cell_clients++;
@@ -2382,8 +2403,6 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
       SilcBool initiator = FALSE;
       SilcBool backup_local = FALSE;
       SilcBool backup_router = FALSE;
-      char *backup_replace_ip = NULL;
-      SilcUInt16 backup_replace_port = 0;
       SilcServerConfigServer *srvconn = entry->sconfig.ref_ptr;
       SilcServerConfigRouter *rconn = entry->rconfig.ref_ptr;
 
@@ -2440,8 +2459,6 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
          initiator = rconn->initiator;
          backup_local = rconn->backup_local;
          backup_router = rconn->backup_router;
-         backup_replace_ip = rconn->backup_replace_ip;
-         backup_replace_port = rconn->backup_replace_port;
        }
       }
 
@@ -2604,6 +2621,8 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
   /* Add connection to server->conns so that we know we have connection
      to this peer. */
   sconn = silc_calloc(1, sizeof(*sconn));
+  if (!sconn)
+    goto out;
   sconn->server = server;
   sconn->sock = sock;
   sconn->remote_host = strdup(hostname);
@@ -2626,6 +2645,14 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
                               param->qos_rate_limit, param->qos_bytes_limit,
                               param->qos_limit_sec, param->qos_limit_usec);
 
+  /* Perform heartbeat */
+  if (param->keepalive_secs) {
+    SILC_LOG_DEBUG(("Perform heartbeat every %d seconds",
+                   param->keepalive_secs));
+    silc_schedule_task_add_timeout(server->schedule, silc_server_do_heartbeat,
+                                  sock, param->keepalive_secs, 0);
+  }
+
   silc_server_config_unref(&entry->cconfig);
   silc_server_config_unref(&entry->sconfig);
   silc_server_config_unref(&entry->rconfig);
@@ -2870,6 +2897,15 @@ static void silc_server_accept_new_connection(SilcNetStatus status,
   entry->op = silc_ske_responder(ske, packet_stream, &params);
 }
 
+/* Perform heartbeat */
+
+SILC_TASK_CALLBACK(silc_server_do_heartbeat)
+{
+  SilcServer server = app_context;
+  SilcPacketStream sock = context;
+  silc_server_send_heartbeat(server, sock);
+}
+
 
 /********************************** Rekey ***********************************/
 
@@ -2905,6 +2941,18 @@ static void silc_server_rekey_completion(SilcSKE ske,
                                 sock, idata->sconn->rekey_timeout, 0);
 }
 
+/* Helper to stop future rekeys on a link. */
+void silc_server_stop_rekey(SilcServer server, SilcClientEntry client)
+{
+  if (!client->connection)
+    return;
+
+  SILC_LOG_DEBUG(("Stopping rekey for client %p", client));
+
+  silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_rekey,
+                               client->connection);
+}
+
 /* Rekey callback.  Start rekey as initiator */
 
 SILC_TASK_CALLBACK(silc_server_do_rekey)
@@ -2985,6 +3033,10 @@ static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
     silc_packet_free(packet);
     return;
   }
+  if (idata->conn_type == SILC_CONN_UNKNOWN) {
+    silc_packet_free(packet);
+    return;
+  }
 
   SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s], sock %p",
                  idata->sconn->remote_host, idata->sconn->remote_port,
@@ -3072,7 +3124,13 @@ void silc_server_disconnect_remote(SilcServer server,
   if (!sock)
     return;
 
-  SILC_LOG_DEBUG(("Disconnecting remote host, sock %p", sock));
+  silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_rekey,
+                               sock);
+  silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_heartbeat,
+                               sock);
+
+  SILC_LOG_DEBUG(("Disconnecting remote host, sock %p, status %d", sock,
+                 status));
 
   va_start(ap, status);
   cp = va_arg(ap, char *);
@@ -3132,7 +3190,13 @@ void silc_server_free_client_data(SilcServer server,
   }
 
   /* Update statistics */
-  server->stat.my_clients--;
+
+  /* Local detached clients aren't counted. */
+  if (!client->local_detached)
+    server->stat.my_clients--;
+  SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                 server->stat.clients - 1));
+  SILC_VERIFY(server->stat.clients > 0);
   server->stat.clients--;
   if (server->stat.cell_clients)
     server->stat.cell_clients--;
@@ -3187,17 +3251,21 @@ void silc_server_free_sock_user_data(SilcServer server,
 
   silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_rekey,
                                sock);
+  silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_heartbeat,
+                               sock);
 
   /* Cancel active protocols */
   if (idata) {
     if (idata->sconn && idata->sconn->op) {
       SILC_LOG_DEBUG(("Abort active protocol"));
       silc_async_abort(idata->sconn->op, NULL, NULL);
+      idata->sconn->op = NULL;
     }
     if (idata->conn_type == SILC_CONN_UNKNOWN &&
         ((SilcUnknownEntry)idata)->op) {
       SILC_LOG_DEBUG(("Abort active protocol"));
       silc_async_abort(((SilcUnknownEntry)idata)->op, NULL, NULL);
+      ((SilcUnknownEntry)idata)->op = NULL;
     }
   }
 
@@ -3944,6 +4012,14 @@ SilcBool silc_server_create_channel_key(SilcServer server,
   if (server->server_type == SILC_ROUTER) {
     if (!channel->rekey)
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    if (!channel->rekey) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      silc_cipher_free(channel->send_key);
+      silc_cipher_free(channel->receive_key);
+      channel->send_key = channel->receive_key = NULL;
+      return FALSE;
+    }
     channel->rekey->channel = channel;
     channel->rekey->key_len = key_len;
     if (channel->rekey->task)
@@ -4057,7 +4133,7 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
       silc_cipher_free(channel->send_key);
       silc_cipher_free(channel->receive_key);
       channel->send_key = channel->receive_key = NULL;
-      return FALSE;
+      return NULL;
     }
   silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
   silc_hmac_set_key(channel->hmac, hash,
@@ -4069,6 +4145,14 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server,
   if (server->server_type == SILC_ROUTER) {
     if (!channel->rekey)
       channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
+    if (!channel->rekey) {
+      memset(channel->key, 0, channel->key_len / 8);
+      silc_free(channel->key);
+      silc_cipher_free(channel->send_key);
+      silc_cipher_free(channel->receive_key);
+      channel->send_key = channel->receive_key = NULL;
+      return NULL;
+    }
     channel->rekey->channel = channel;
     if (channel->rekey->task)
       silc_schedule_task_del(server->schedule, channel->rekey->task);
@@ -5008,10 +5092,14 @@ void silc_server_save_users_on_channel(SilcServer server,
       }
 
       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+      SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+                     server->stat.clients + 1));
+      server->stat.clients++;
     }
 
     if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
-      SILC_LOG_ERROR(("Attempting to add unregistered client to channel ",
+      SILC_LOG_ERROR(("Attempting to add unregistered client to channel "
                      "%s", channel->channel_name));
       continue;
     }
@@ -5019,6 +5107,8 @@ void silc_server_save_users_on_channel(SilcServer server,
     if (!silc_server_client_on_channel(client, channel, &chl)) {
       /* Client was not on the channel, add it. */
       chl = silc_calloc(1, sizeof(*chl));
+      if (!chl)
+        continue;
       chl->client = client;
       chl->mode = mode;
       chl->channel = channel;
@@ -5097,6 +5187,8 @@ void silc_server_save_user_channels(SilcServer server,
       /* Add the client on the channel */
       if (!silc_server_client_on_channel(client, channel, &chl)) {
        chl = silc_calloc(1, sizeof(*chl));
+       if (!chl)
+         continue;
        chl->client = client;
        chl->mode = chumodes[i++];
        chl->channel = channel;
@@ -5262,6 +5354,8 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
     buffer = silc_buffer_realloc(buffer,
                                 (buffer ?
                                  silc_buffer_truelen(buffer) + len : len));
+    if (!buffer)
+      return NULL;
     silc_buffer_pull_tail(buffer, (buffer->end - buffer->data));
     silc_buffer_format(buffer,
                       SILC_STR_UI_SHORT(name_len),
@@ -5277,6 +5371,8 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server,
        silc_buffer_realloc(*user_mode_list,
                            (*user_mode_list ?
                             silc_buffer_truelen((*user_mode_list)) + 4 : 4));
+      if (!(*user_mode_list))
+        return NULL;
       silc_buffer_pull_tail(*user_mode_list, ((*user_mode_list)->end -
                                              (*user_mode_list)->data));
       SILC_PUT32_MSB(chl->mode, (*user_mode_list)->data);