SILC_TASK_CALLBACK(silc_server_channel_key_rekey);
SILC_TASK_CALLBACK(silc_server_failure_callback);
SILC_TASK_CALLBACK(silc_server_rekey_callback);
+SILC_TASK_CALLBACK(silc_server_get_stats);
/* Allocates a new SILC server object. This has to be done before the server
can be used. After allocation one must call silc_server_init to initialize
SILC_LOG_DEBUG(("Initializing server"));
+ server->starttime = time(NULL);
+
/* Take config object for us */
silc_server_config_ref(&server->config_ref, server->config,
server->config);
(void *)purge, purge->timeout, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ /* If we are normal server we'll retrieve network statisticial information
+ once in a while from the router. */
+ if (server->server_type == SILC_SERVER)
+ silc_schedule_task_add(purge->schedule, 0, silc_server_get_stats,
+ server, 10, 0, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
SILC_LOG_DEBUG(("Server initialized"));
/* We are done here, return succesfully */
return TRUE;
err:
+ silc_server_config_unref(&server->config_ref);
silc_net_close_server(sock);
return FALSE;
}
bool silc_server_rehash(SilcServer server)
{
SilcServerConfig newconfig;
- SilcUInt32 max_conns;
SILC_LOG_INFO(("Rehashing server"));
return FALSE;
}
- max_conns = server->config->param.connections_max;
-
- /* Our old config is gone now. We'll unreference our reference made in
- silc_server_init and then destroy it since we are destroying it
- underneath the application (layer which called silc_server_init). */
- silc_server_config_unref(&server->config_ref);
- silc_server_config_destroy(server->config);
-
- /* Take new config context */
- server->config = newconfig;
- silc_server_config_ref(&server->config_ref, server->config, server->config);
-
/* Reinit scheduler if necessary */
- if (server->config->param.connections_max > max_conns)
- silc_schedule_reinit(server->schedule,
- server->config->param.connections_max);
+ if (newconfig->param.connections_max > server->config->param.connections_max)
+ if (!silc_schedule_reinit(server->schedule,
+ newconfig->param.connections_max))
+ return FALSE;
/* Fix the server_name field */
if (strcmp(server->server_name, newconfig->server_info->server_name)) {
/* Update the idcache list with a fresh pointer */
silc_free(server->id_entry->server_name);
server->id_entry->server_name = strdup(server->server_name);
- silc_idcache_del_by_context(server->local_list->servers, server->id_entry);
- silc_idcache_add(server->local_list->servers,
- server->id_entry->server_name,
- server->id_entry->id, server->id_entry, 0, NULL);
+ if (!silc_idcache_del_by_context(server->local_list->servers,
+ server->id_entry))
+ return FALSE;
+ if (!silc_idcache_add(server->local_list->servers,
+ server->id_entry->server_name,
+ server->id_entry->id, server->id_entry, 0, NULL))
+ return FALSE;
}
+ /* Set logging */
silc_server_config_setlogfiles(server);
/* Change new key pair if necessary */
newconfig->server_info->public_key)) {
silc_pkcs_public_key_free(server->public_key);
silc_pkcs_private_key_free(server->private_key);
- server->public_key = server->config->server_info->public_key;
- server->private_key = server->config->server_info->private_key;
- server->config->server_info->public_key = NULL;
- server->config->server_info->private_key = NULL;
+ server->public_key = newconfig->server_info->public_key;
+ server->private_key = newconfig->server_info->private_key;
+ newconfig->server_info->public_key = NULL;
+ newconfig->server_info->private_key = NULL;
/* Allocate PKCS context for local public and private keys */
silc_pkcs_free(server->pkcs);
SILC_TASK_PRI_NORMAL);
/* Check whether our router status has changed */
- if (server->config->servers) {
- SilcServerConfigServer *ptr = server->config->servers;
+ if (newconfig->servers) {
+ SilcServerConfigServer *ptr = newconfig->servers;
server->server_type = SILC_ROUTER;
while (ptr) {
}
}
+ /* Our old config is gone now. We'll unreference our reference made in
+ silc_server_init and then destroy it since we are destroying it
+ underneath the application (layer which called silc_server_init). */
+ silc_server_config_unref(&server->config_ref);
+ silc_server_config_destroy(server->config);
+
+ /* Take new config context */
+ server->config = newconfig;
+ silc_server_config_ref(&server->config_ref, server->config, server->config);
+
SILC_LOG_DEBUG(("Server rehashed"));
return TRUE;
return;
}
+ /* Cancel any possible retry timeouts */
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_router);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_to_router_retry);
+
/* Create the connections to all our routes */
for (ptr = server->config->routers; ptr; ptr = ptr->next) {
/* Statistics */
server->stat.my_clients++;
server->stat.clients++;
- if (server->server_type == SILC_ROUTER)
- server->stat.cell_clients++;
+ server->stat.cell_clients++;
/* Get connection parameters */
if (conn->param) {
silc_server_ftp(server, sock, packet);
break;
+ case SILC_PACKET_RESUME_CLIENT:
+ /* Resume client */
+ SILC_LOG_DEBUG(("Resume Client packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_resume_client(server, sock, packet);
+ break;
+
case SILC_PACKET_RESUME_ROUTER:
/* Resume router packet received. This packet is received for backup
router resuming protocol. */
/* Update statistics */
server->stat.my_clients--;
server->stat.clients--;
- if (server->server_type == SILC_ROUTER)
+ if (server->stat.cell_clients)
server->stat.cell_clients--;
SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
(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;
- client->mode = 0;
}
/* Frees user_data pointer from socket connection object. This also sends
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER)
+ entry->users_resolved = TRUE;
+
return entry;
}
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER)
+ entry->users_resolved = TRUE;
+
return entry;
}
SilcClientID *client_id;
SilcClientEntry client;
SilcIDCacheEntry cache;
+ SilcChannelClientEntry chl;
bool global;
SILC_LOG_DEBUG(("Start"));
silc_free(client_id);
- if (!silc_server_client_on_channel(client, channel, NULL)) {
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
/* Client was not on the channel, add it. */
- SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+ chl = silc_calloc(1, sizeof(*chl));
chl->client = client;
chl->mode = mode;
chl->channel = channel;
silc_hash_table_add(channel->user_list, chl->client, chl);
silc_hash_table_add(client->channels, chl->channel, chl);
channel->user_count++;
+ } else {
+ /* Update mode */
+ chl->mode = mode;
}
}
}
+/* Saves channels and channels user modes to the `client'. Removes
+ the client from those channels that are not sent in the list but
+ it has joined. */
+
+void silc_server_save_user_channels(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientEntry client,
+ SilcBuffer channels,
+ SilcBuffer channels_user_modes)
+{
+ SilcDList ch;
+ SilcUInt32 *chumodes;
+ SilcChannelPayload entry;
+ SilcChannelEntry channel;
+ SilcChannelID *channel_id;
+ SilcChannelClientEntry chl;
+ SilcHashTable ht = NULL;
+ SilcHashTableList htl;
+ char *name;
+ int i = 0;
+
+ if (!channels ||!channels_user_modes)
+ goto out;
+
+ ch = silc_channel_payload_parse_list(channels->data, channels->len);
+ if (ch && silc_get_mode_list(channels_user_modes, silc_dlist_count(ch),
+ &chumodes)) {
+ ht = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
+ NULL, NULL, NULL, TRUE);
+ silc_dlist_start(ch);
+ while ((entry = silc_dlist_get(ch)) != SILC_LIST_END) {
+ /* Check if we have this channel, and add it if we don't have it.
+ Also add the client on the channel unless it is there already. */
+ channel_id = silc_channel_get_id_parse(entry);
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel)
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ if (server->server_type != SILC_SERVER) {
+ silc_free(channel_id);
+ i++;
+ continue;
+ }
+
+ /* We don't have that channel anywhere, add it. */
+ name = silc_channel_get_name(entry, NULL);
+ channel = silc_idlist_add_channel(server->global_list, strdup(name), 0,
+ channel_id, server->router,
+ NULL, NULL, 0);
+ if (!channel) {
+ silc_free(channel_id);
+ i++;
+ continue;
+ }
+ channel_id = NULL;
+ }
+
+ channel->mode = silc_channel_get_mode(entry);
+
+ /* Add the client on the channel */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ chl = silc_calloc(1, sizeof(*chl));
+ chl->client = client;
+ chl->mode = chumodes[i++];
+ chl->channel = channel;
+ silc_hash_table_add(channel->user_list, chl->client, chl);
+ silc_hash_table_add(client->channels, chl->channel, chl);
+ channel->user_count++;
+ } else {
+ /* Update mode */
+ chl->mode = chumodes[i++];
+ }
+
+ silc_hash_table_add(ht, channel, channel);
+ silc_free(channel_id);
+ }
+ silc_channel_payload_list_free(ch);
+ silc_free(chumodes);
+ }
+
+ out:
+ /* Go through the list again and remove client from channels that
+ are no part of the list. */
+ if (ht) {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ if (!silc_hash_table_find(ht, chl->channel, NULL, NULL)) {
+ silc_hash_table_del(chl->channel->user_list, chl->client);
+ silc_hash_table_del(chl->client->channels, chl->channel);
+ silc_free(chl);
+ }
+ }
+ silc_hash_table_list_reset(&htl);
+ silc_hash_table_free(ht);
+ } else {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ silc_hash_table_del(chl->channel->user_list, chl->client);
+ silc_hash_table_del(chl->client->channels, chl->channel);
+ silc_free(chl);
+ }
+ silc_hash_table_list_reset(&htl);
+ }
+}
+
/* Lookups route to the client indicated by the `id_data'. The connection
object and internal data object is returned. Returns NULL if route
could not be found to the client. If the `client_id' is specified then
it is used and the `id_data' is ignored. */
-SilcSocketConnection silc_server_get_client_route(SilcServer server,
- unsigned char *id_data,
- SilcUInt32 id_len,
- SilcClientID *client_id,
- SilcIDListData *idata)
+SilcSocketConnection
+silc_server_get_client_route(SilcServer server,
+ unsigned char *id_data,
+ SilcUInt32 id_len,
+ SilcClientID *client_id,
+ SilcIDListData *idata,
+ SilcClientEntry *client_entry)
{
SilcClientID *id;
SilcClientEntry client;
SILC_LOG_DEBUG(("Start"));
+ if (client_entry)
+ *client_entry = NULL;
+
/* Decode destination Client ID */
if (!client_id) {
id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
/* Seems that client really is directly connected to us */
if (idata)
*idata = (SilcIDListData)client;
+ if (client_entry)
+ *client_entry = client;
return client->connection;
}
Secret channels are not put to the list. */
SilcBuffer silc_server_get_client_channel_list(SilcServer server,
- SilcClientEntry client)
+ SilcClientEntry client,
+ bool get_private,
+ bool get_secret,
+ SilcBuffer *user_mode_list)
{
SilcBuffer buffer = NULL;
SilcChannelEntry channel;
SilcUInt16 name_len;
int len;
+ if (user_mode_list)
+ *user_mode_list = NULL;
+
silc_hash_table_list(client->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
- if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
- channel->mode & SILC_CHANNEL_MODE_PRIVATE)
+ if (channel->mode & SILC_CHANNEL_MODE_SECRET && !get_secret)
+ continue;
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVATE && !get_private)
continue;
cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
len = 4 + name_len + id_len + 4;
buffer = silc_buffer_realloc(buffer,
- (buffer ? (buffer)->truelen + len : len));
- silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+ (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(id_len),
SILC_STR_UI_XNSTRING(cid, id_len),
- SILC_STR_UI_INT(chl->mode), /* Client's mode */
+ SILC_STR_UI_INT(chl->channel->mode),
SILC_STR_END);
silc_buffer_pull(buffer, len);
silc_free(cid);
+
+ if (user_mode_list) {
+ *user_mode_list = silc_buffer_realloc(*user_mode_list,
+ (*user_mode_list ?
+ (*user_mode_list)->truelen + 4 :
+ 4));
+ silc_buffer_pull_tail(*user_mode_list, ((*user_mode_list)->end -
+ (*user_mode_list)->data));
+ SILC_PUT32_MSB(chl->mode, (*user_mode_list)->data);
+ silc_buffer_pull(*user_mode_list, 4);
+ }
}
silc_hash_table_list_reset(&htl);
if (buffer)
silc_buffer_push(buffer, buffer->data - buffer->head);
+ if (user_mode_list && *user_mode_list)
+ silc_buffer_push(*user_mode_list, ((*user_mode_list)->data -
+ (*user_mode_list)->head));
return buffer;
}
SilcClientEntry silc_server_get_client_resolve(SilcServer server,
SilcClientID *client_id,
+ bool always_resolve,
bool *resolved)
{
SilcClientEntry client;
if (!client && server->standalone)
return NULL;
- if (!client || !client->nickname || !client->username) {
+ if (!client || !client->nickname || !client->username ||
+ always_resolve) {
SilcBuffer buffer, idp;
- client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
- client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
- client->resolve_cmd_ident = ++server->cmd_ident;
+ 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,
silc_ske_free(ctx->ske);
silc_free(ctx);
}
+
+/* Task callback used to retrieve network statistical information from
+ router server once in a while. */
+
+SILC_TASK_CALLBACK(silc_server_get_stats)
+{
+ SilcServer server = (SilcServer)context;
+ SilcBuffer idp, packet;
+
+ SILC_LOG_DEBUG(("Retrieving stats from router"));
+
+ if (!server->standalone) {
+ idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+ packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+ ++server->cmd_ident, 1,
+ 1, idp->data, idp->len);
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_COMMAND, 0, packet->data,
+ packet->len, FALSE);
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+ }
+
+ silc_schedule_task_add(server->schedule, 0, silc_server_get_stats,
+ server, 120, 0, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+}