updates.
[runtime.git] / apps / silcd / server.c
index 849fc10bbeaeb085cb4f008d4467ce1ff9b0aedc..6df25f7bc93f276b37709b315996fe84704a7557 100644 (file)
@@ -113,6 +113,7 @@ int silc_server_init(SilcServer server)
   int *sock = NULL, sock_count = 0, i;
   SilcServerID *id;
   SilcServerEntry id_entry;
+  SilcIDListPurge purge;
 
   SILC_LOG_DEBUG(("Initializing server"));
   assert(server);
@@ -335,6 +336,27 @@ int silc_server_init(SilcServer server)
   if (server->config->servers)
     server->server_type = SILC_ROUTER;
 
+  /* Register the ID Cache purge task. This periodically purges the ID cache
+     and removes the expired cache entries. */
+
+  /* Clients local list */
+  purge = silc_calloc(1, sizeof(*purge));
+  purge->cache = server->local_list->clients;
+  purge->timeout_queue = server->timeout_queue;
+  silc_task_register(purge->timeout_queue, 0, 
+                    silc_idlist_purge,
+                    (void *)purge, 600, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+  /* Clients global list */
+  purge = silc_calloc(1, sizeof(*purge));
+  purge->cache = server->global_list->clients;
+  purge->timeout_queue = server->timeout_queue;
+  silc_task_register(purge->timeout_queue, 0, 
+                    silc_idlist_purge,
+                    (void *)purge, 300, 0,
+                    SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
@@ -1028,7 +1050,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     silc_ske_free_key_material(ctx->keymat);
@@ -1126,7 +1149,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+  if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+      protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
     /* Error occured during protocol */
     silc_protocol_free(protocol);
     if (ctx->packet)
@@ -1160,7 +1184,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
         and other information is created after we have received NEW_CLIENT
         packet from client. */
       client = silc_idlist_add_client(server->local_list, 
-                                     NULL, NULL, NULL, NULL, NULL, sock);
+                                     NULL, 0, NULL, NULL, NULL, NULL, sock);
       if (!client) {
        SILC_LOG_ERROR(("Could not add new client to cache"));
        silc_free(sock->user_data);
@@ -1291,7 +1315,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     server->stat.packets_sent++;
 
     if (sock->outbuf->data - sock->outbuf->head)
-      silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
 
     ret = silc_server_packet_send_real(server, sock, TRUE);
 
@@ -1971,19 +1995,41 @@ SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
 
 void silc_server_free_client_data(SilcServer server, 
                                  SilcSocketConnection sock,
-                                 SilcClientEntry client, char *signoff)
+                                 SilcClientEntry client, 
+                                 int notify,
+                                 char *signoff)
 {
   FreeClientInternal i = silc_calloc(1, sizeof(*i));
 
-  /* Send REMOVE_ID packet to routers. */
-  if (!server->standalone && server->router)
+  /* If there is pending outgoing data for the client then purge it
+     to the network before removing the client entry. */
+  if (SILC_IS_OUTBUF_PENDING(sock) && (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+    server->stat.packets_sent++;
+
+    if (sock->outbuf->data - sock->outbuf->head)
+     silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+    silc_server_packet_send_real(server, sock, TRUE);
+
+    SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+    SILC_UNSET_OUTBUF_PENDING(sock);
+    silc_buffer_clear(sock->outbuf);
+  }
+
+  /* Send SIGNOFF notify to routers. */
+  if (notify && !server->standalone && server->router)
     silc_server_send_notify_signoff(server, server->router->connection,
                                    server->server_type == SILC_SERVER ?
                                    FALSE : TRUE, client->id, 
                                    SILC_ID_CLIENT_LEN, signoff);
 
   /* Remove client from all channels */
-  silc_server_remove_from_channels(server, sock, client, signoff);
+  if (notify)
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    TRUE, signoff, TRUE);
+  else
+    silc_server_remove_from_channels(server, NULL, client, 
+                                    FALSE, NULL, FALSE);
 
   /* We will not delete the client entry right away. We will take it
      into history (for WHOWAS command) for 5 minutes */
@@ -2015,7 +2061,7 @@ void silc_server_free_sock_user_data(SilcServer server,
   case SILC_SOCKET_TYPE_CLIENT:
     {
       SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-      silc_server_free_client_data(server, sock, user_data, NULL);
+      silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
       break;
     }
   case SILC_SOCKET_TYPE_SERVER:
@@ -2094,7 +2140,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, NULL);
+       silc_server_remove_from_channels(server, NULL, client, TRUE, 
+                                        NULL, TRUE);
        silc_idlist_del_client(server->local_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2119,7 +2166,8 @@ int silc_server_remove_clients_by_server(SilcServer server,
        }
 
        /* Remove the client entry */
-       silc_server_remove_from_channels(server, NULL, client, NULL);
+       silc_server_remove_from_channels(server, NULL, client, TRUE,
+                                        NULL, TRUE);
        silc_idlist_del_client(server->global_list, client);
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -2171,7 +2219,9 @@ int silc_server_channel_has_local(SilcChannelEntry channel)
 void silc_server_remove_from_channels(SilcServer server, 
                                      SilcSocketConnection sock,
                                      SilcClientEntry client,
-                                     char *signoff_message)
+                                     int notify,
+                                     char *signoff_message,
+                                     int keygen)
 {
   SilcChannelEntry channel;
   SilcChannelClientEntry chl;
@@ -2218,7 +2268,7 @@ void silc_server_remove_from_channels(SilcServer server,
     if (server->server_type == SILC_SERVER &&
        !silc_server_channel_has_local(channel)) {
       /* Notify about leaving client if this channel has global users. */
-      if (channel->global_users)
+      if (notify && channel->global_users)
        silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
                                           SILC_NOTIFY_TYPE_SIGNOFF, 
                                           signoff_message ? 2 : 1,
@@ -2234,12 +2284,24 @@ void silc_server_remove_from_channels(SilcServer server,
 
     /* Send notify to channel about client leaving SILC and thus
        the entire channel. */
-    silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
-                                      SILC_NOTIFY_TYPE_SIGNOFF, 
-                                      signoff_message ? 2 : 1,
-                                      clidp->data, clidp->len,
-                                      signoff_message, signoff_message ?
-                                      strlen(signoff_message) : 0);
+    if (notify)
+      silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+                                        SILC_NOTIFY_TYPE_SIGNOFF, 
+                                        signoff_message ? 2 : 1,
+                                        clidp->data, clidp->len,
+                                        signoff_message, signoff_message ?
+                                        strlen(signoff_message) : 0);
+
+    if (keygen) {
+      /* Re-generate channel key */
+      silc_server_create_channel_key(server, channel, 0);
+      
+      /* Send the channel key to the channel. The key of course is not sent
+        to the client who was removed f rom the channel. */
+      silc_server_send_channel_key(server, client->connection, channel, 
+                                  server->server_type == SILC_ROUTER ? 
+                                  FALSE : !server->standalone);
+    }
   }
 
   silc_buffer_free(clidp);
@@ -2420,10 +2482,10 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server,
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE) {
+  if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
-  }
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
 
   server->stat.my_channels++;
 
@@ -2478,10 +2540,10 @@ silc_server_create_new_channel_with_id(SilcServer server,
 
   /* Notify other routers about the new channel. We send the packet
      to our primary route. */
-  if (broadcast && server->standalone == FALSE) {
+  if (broadcast && server->standalone == FALSE)
     silc_server_send_new_channel(server, server->router->connection, TRUE, 
-                                channel_name, entry->id, SILC_ID_CHANNEL_LEN);
-  }
+                                channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+                                entry->mode);
 
   server->stat.my_channels++;
 
@@ -2500,6 +2562,8 @@ void silc_server_create_channel_key(SilcServer server,
   unsigned char channel_key[32], hash[32];
   unsigned int len;
 
+  SILC_LOG_DEBUG(("Generating channel key"));
+
   if (!channel->channel_key)
     if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
       return;
@@ -2819,7 +2883,7 @@ static void silc_server_announce_get_channels(SilcServer server,
        cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
        name_len = strlen(channel->channel_name);
 
-       len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+       len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
        *channels = 
          silc_buffer_realloc(*channels, 
                              (*channels ? (*channels)->truelen + len : len));
@@ -2831,6 +2895,7 @@ static void silc_server_announce_get_channels(SilcServer server,
                                                name_len),
                           SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
                           SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                          SILC_STR_UI_INT(0),
                           SILC_STR_END);
        silc_buffer_pull(*channels, len);
 
@@ -3037,13 +3102,16 @@ void silc_server_save_users_on_channel(SilcServer server,
       /* We don't have that client anywhere, add it. The client is added
         to global list since server didn't have it in the lists so it must be 
         global. */
-      client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL, 
+      client = silc_idlist_add_client(server->global_list, NULL, 0, NULL, 
+                                     NULL, 
                                      silc_id_dup(client_id, SILC_ID_CLIENT), 
                                      sock->user_data, NULL);
       if (!client) {
        silc_free(client_id);
        continue;
       }
+
+      client->data.registered = TRUE;
     }
 
     silc_free(client_id);
@@ -3059,3 +3127,121 @@ void silc_server_save_users_on_channel(SilcServer server,
     }
   }
 }
+
+/* Lookups route to the client indicated by `id' client ID. The connection
+   object and internal data object is returned. Returns NULL if route
+   could not be found to the client. */
+
+SilcSocketConnection silc_server_get_client_route(SilcServer server,
+                                                 unsigned char *id_data,
+                                                 unsigned int id_len,
+                                                 SilcIDListData *idata)
+{
+  SilcClientID *id;
+  SilcClientEntry client;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Decode destination Client ID */
+  id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
+  if (!id) {
+    SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+    return NULL;
+  }
+
+  /* If the destination belongs to our server we don't have to route
+     the packet anywhere but to send it to the local destination. */
+  client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+  if (client) {
+    silc_free(id);
+
+    if (client && client->data.registered == FALSE)
+      return NULL;
+
+    /* If we are router and the client has router then the client is in
+       our cell but not directly connected to us. */
+    if (server->server_type == SILC_ROUTER && client->router) {
+      /* We are of course in this case the client's router thus the real
+        "router" of the client is the server who owns the client. Thus
+        we will send the packet to that server. */
+      *idata = (SilcIDListData)client->router;
+      return client->router->connection;
+    }
+
+    /* Seems that client really is directly connected to us */
+    *idata = (SilcIDListData)client;
+    return client->connection;
+  }
+
+  /* Destination belongs to someone not in this server. If we are normal
+     server our action is to send the packet to our router. */
+  if (server->server_type == SILC_SERVER && !server->standalone) {
+    silc_free(id);
+    *idata = (SilcIDListData)server->router;
+    return server->router->connection;
+  }
+
+  /* We are router and we will perform route lookup for the destination 
+     and send the packet to fastest route. */
+  if (server->server_type == SILC_ROUTER && !server->standalone) {
+    /* Check first that the ID is valid */
+    client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+    if (client) {
+      SilcSocketConnection dst_sock;
+
+      dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+
+      silc_free(id);
+      *idata = (SilcIDListData)dst_sock->user_data;
+      return dst_sock;
+    }
+  }
+
+  silc_free(id);
+  return NULL;
+}
+
+/* Encodes and returns channel list of channels the `client' has joined.
+   Secret channels are not put to the list. */
+
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+                                              SilcClientEntry client)
+{
+  SilcBuffer buffer = NULL;
+  SilcChannelEntry channel;
+  SilcChannelClientEntry chl;
+  unsigned char *cid;
+  unsigned short name_len;
+  int len;
+
+  silc_list_start(client->channels);
+  while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+    channel = chl->channel;
+
+    if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+      continue;
+
+    cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+    name_len = strlen(channel->channel_name);
+    
+    len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+    buffer = silc_buffer_realloc(buffer, 
+                                (buffer ? (buffer)->truelen + len : len));
+    silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+    silc_buffer_format(buffer,
+                      SILC_STR_UI_SHORT(name_len),
+                      SILC_STR_UI_XNSTRING(channel->channel_name, 
+                                           name_len),
+                      SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+                      SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+                      SILC_STR_UI_INT(chl->mode), /* Client's mode */
+                      SILC_STR_END);
+    silc_buffer_pull(buffer, len);
+    silc_free(cid);
+  }
+
+  if (buffer)
+    silc_buffer_push(buffer, buffer->data - buffer->head);
+
+  return buffer;
+}