/*
- server.c
+ server.c
Author: Pekka Riikonen <priikone@silcnet.org>
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
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+ the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
#include "server_internal.h"
/* Static prototypes */
+SILC_TASK_CALLBACK(silc_server_rehash_close_connection);
SILC_TASK_CALLBACK(silc_server_connect_to_router_retry);
SILC_TASK_CALLBACK(silc_server_connect_router);
SILC_TASK_CALLBACK(silc_server_connect_to_router);
SILC_TASK_CALLBACK(silc_server_timeout_remote);
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
void silc_server_free(SilcServer server)
{
- if (server) {
+ SilcIDCacheList list;
+ SilcIDCacheEntry cache;
+
+ if (!server)
+ return;
+
#ifdef SILC_SIM
+ {
SilcSim sim;
-
+ silc_dlist_start(server->sim);
while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
silc_dlist_del(server->sim, sim);
+ silc_sim_close(sim);
silc_sim_free(sim);
}
silc_dlist_uninit(server->sim);
+ }
#endif
- silc_server_config_unref(&server->config_ref);
- if (server->rng)
- silc_rng_free(server->rng);
- if (server->pkcs)
- silc_pkcs_free(server->pkcs);
- if (server->public_key)
- silc_pkcs_public_key_free(server->public_key);
- if (server->private_key)
- silc_pkcs_private_key_free(server->private_key);
- if (server->pending_commands)
- silc_dlist_uninit(server->pending_commands);
- if (server->id_entry)
- silc_idlist_del_server(server->local_list, server->id_entry);
-
- silc_idcache_free(server->local_list->clients);
- silc_idcache_free(server->local_list->servers);
- silc_idcache_free(server->local_list->channels);
- silc_idcache_free(server->global_list->clients);
- silc_idcache_free(server->global_list->servers);
- silc_idcache_free(server->global_list->channels);
- silc_hash_table_free(server->watcher_list);
+ silc_server_backup_free(server);
+ silc_server_config_unref(&server->config_ref);
+ if (server->rng)
+ silc_rng_free(server->rng);
+ if (server->pkcs)
+ silc_pkcs_free(server->pkcs);
+ if (server->public_key)
+ silc_pkcs_public_key_free(server->public_key);
+ if (server->private_key)
+ silc_pkcs_private_key_free(server->private_key);
+ if (server->pending_commands)
+ silc_dlist_uninit(server->pending_commands);
+ if (server->id_entry)
+ silc_idlist_del_server(server->local_list, server->id_entry);
+
+ /* Delete all channels */
+ list = NULL;
+ if (silc_idcache_get_all(server->local_list->channels, &list) &&
+ silc_idcache_list_first(list, &cache)) {
+ silc_idlist_del_channel(server->local_list, cache->context);
+ while (silc_idcache_list_next(list, &cache))
+ silc_idlist_del_channel(server->local_list, cache->context);
+ }
+ if (list)
+ silc_idcache_list_free(list);
+ list = NULL;
+ if (silc_idcache_get_all(server->global_list->channels, &list) &&
+ silc_idcache_list_first(list, &cache)) {
+ silc_idlist_del_channel(server->global_list, cache->context);
+ while (silc_idcache_list_next(list, &cache))
+ silc_idlist_del_channel(server->global_list, cache->context);
+ }
+ if (list)
+ silc_idcache_list_free(list);
+
+ /* Delete all clients */
+ list = NULL;
+ if (silc_idcache_get_all(server->local_list->clients, &list) &&
+ silc_idcache_list_first(list, &cache)) {
+ silc_idlist_del_client(server->local_list, cache->context);
+ while (silc_idcache_list_next(list, &cache))
+ silc_idlist_del_client(server->local_list, cache->context);
+ }
+ if (list)
+ silc_idcache_list_free(list);
+ list = NULL;
+ if (silc_idcache_get_all(server->global_list->clients, &list) &&
+ silc_idcache_list_first(list, &cache)) {
+ silc_idlist_del_client(server->global_list, cache->context);
+ while (silc_idcache_list_next(list, &cache))
+ silc_idlist_del_client(server->global_list, cache->context);
+ }
+ if (list)
+ silc_idcache_list_free(list);
- silc_free(server->sockets);
- silc_free(server);
+ /* Delete all servers */
+ list = NULL;
+ if (silc_idcache_get_all(server->local_list->servers, &list) &&
+ silc_idcache_list_first(list, &cache)) {
+ silc_idlist_del_server(server->local_list, cache->context);
+ while (silc_idcache_list_next(list, &cache))
+ silc_idlist_del_server(server->local_list, cache->context);
+ }
+ if (list)
+ silc_idcache_list_free(list);
+ list = NULL;
+ if (silc_idcache_get_all(server->global_list->servers, &list) &&
+ silc_idcache_list_first(list, &cache)) {
+ silc_idlist_del_server(server->global_list, cache->context);
+ while (silc_idcache_list_next(list, &cache))
+ silc_idlist_del_server(server->global_list, cache->context);
}
+ if (list)
+ silc_idcache_list_free(list);
+
+ silc_idcache_free(server->local_list->clients);
+ silc_idcache_free(server->local_list->servers);
+ silc_idcache_free(server->local_list->channels);
+ silc_idcache_free(server->global_list->clients);
+ silc_idcache_free(server->global_list->servers);
+ silc_idcache_free(server->global_list->channels);
+ silc_hash_table_free(server->watcher_list);
+
+ silc_hash_free(server->md5hash);
+ silc_hash_free(server->sha1hash);
+ silc_hmac_unregister_all();
+ silc_hash_unregister_all();
+ silc_cipher_unregister_all();
+ silc_pkcs_unregister_all();
+
+ silc_free(server->local_list);
+ silc_free(server->global_list);
+ silc_free(server->server_name);
+ silc_free(server->id_string);
+ silc_free(server->purge_i);
+ silc_free(server->purge_g);
+ silc_free(server);
}
/* Creates a new server listener. */
{
*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;
}
}
/* Adds a secondary listener. */
+
bool silc_server_init_secondary(SilcServer server)
{
- int sock=0, sock_list[server->config->param.connections_max];
+ int sock = 0, sock_list[server->config->param.connections_max];
SilcSocketConnection newsocket = NULL;
SilcServerConfigServerInfoInterface *interface;
for (interface = server->config->server_info->secondary; interface;
interface = interface->next, sock++) {
-
+
if (!silc_server_listen(server,
interface->server_ip, interface->port, &sock_list[sock]))
goto err;
/* Add ourselves also to the socket table. The entry allocated above
is sent as argument for fast referencing in the future. */
- silc_socket_alloc(sock_list[sock],
- SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+ silc_socket_alloc(sock_list[sock],
+ SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
server->sockets[sock_list[sock]] = newsocket;
+ SILC_SET_LISTENER(newsocket);
/* Perform name and address lookups to resolve the listenning address
and port. */
newsocket->hostname = strdup(newsocket->ip);
}
newsocket->port = silc_net_get_local_port(sock);
-
+
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;
-
-err:
+ err:
do silc_net_close_server(sock_list[sock--]); while (sock >= 0);
return FALSE;
-
}
/* Initializes the entire SILC server. This is called always before running
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 */
silc_pkcs_private_key_set(server->pkcs, server->private_key);
/* Initialize the scheduler */
- server->schedule = silc_schedule_init(server->config->param.connections_max);
+ server->schedule = silc_schedule_init(server->config->param.connections_max,
+ server);
if (!server->schedule)
goto err;
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);
is sent as argument for fast referencing in the future. */
silc_socket_alloc(sock, SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
server->sockets[sock] = newsocket;
+ SILC_SET_LISTENER(newsocket);
/* Perform name and address lookups to resolve the listenning address
and port. */
server->id = id;
server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
- server->id_type = SILC_ID_SERVER;
server->server_name = server->config->server_info->server_name;
server->config->server_info->server_name = NULL;
and removes the expired cache entries. */
/* Clients local list */
- purge = silc_calloc(1, sizeof(*purge));
+ server->purge_i = purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->local_list->clients;
- purge->schedule = server->schedule;
purge->timeout = 600;
- silc_schedule_task_add(purge->schedule, 0,
- silc_idlist_purge,
+ silc_schedule_task_add(server->schedule, 0, silc_idlist_purge,
(void *)purge, purge->timeout, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
/* Clients global list */
- purge = silc_calloc(1, sizeof(*purge));
+ server->purge_g = purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->global_list->clients;
- purge->schedule = server->schedule;
purge->timeout = 300;
- silc_schedule_task_add(purge->schedule, 0,
- silc_idlist_purge,
+ silc_schedule_task_add(server->schedule, 0, silc_idlist_purge,
(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,
+ if (server->server_type != SILC_ROUTER)
+ silc_schedule_task_add(server->schedule, 0, silc_server_get_stats,
server, 10, 0, SILC_TASK_TIMEOUT,
SILC_TASK_PRI_LOW);
return FALSE;
}
+/* Task callback to close a socket connection after rehash */
+
+SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
+{
+ SilcServer server = context;
+ SilcSocketConnection sock = server->sockets[fd];
+
+ if (!sock)
+ return;
+
+ SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
+ sock->hostname, sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
+ silc_schedule_task_del_by_context(server->schedule, sock);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER,
+ "This connection is removed from "
+ "configuration");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+}
+
/* This function basically reads the config file again and switches the config
object pointed by the server object. After that, we have to fix various
things such as the server_name and the listening ports.
silc_pkcs_private_key_set(server->pkcs, server->private_key);
}
+ /* Check for unconfigured server and router connections and close
+ connections that were unconfigured. */
+
+ if (server->config->routers) {
+ SilcServerConfigRouter *ptr;
+ SilcServerConfigRouter *newptr;
+ bool found;
+
+ for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+ found = FALSE;
+
+ /* Check whether new config has this one too */
+ for (newptr = newconfig->routers; newptr; newptr = newptr->next) {
+ if (silc_string_compare(newptr->host, ptr->host) &&
+ newptr->port == ptr->port &&
+ newptr->initiator == ptr->initiator) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found && ptr->host) {
+ /* Remove this connection */
+ SilcSocketConnection sock;
+ sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
+ ptr->host, ptr->port);
+ if (sock && !SILC_IS_LISTENER(sock))
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rehash_close_connection,
+ server, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ }
+ }
+ }
+
+ if (server->config->servers) {
+ SilcServerConfigServer *ptr;
+ SilcServerConfigServer *newptr;
+ bool found;
+
+ for (ptr = server->config->servers; ptr; ptr = ptr->next) {
+ found = FALSE;
+
+ /* Check whether new config has this one too */
+ for (newptr = newconfig->servers; newptr; newptr = newptr->next) {
+ if (silc_string_compare(newptr->host, ptr->host)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found && ptr->host) {
+ /* Remove this connection */
+ SilcSocketConnection sock;
+ sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_SERVER,
+ ptr->host, 0);
+ if (sock && !SILC_IS_LISTENER(sock))
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rehash_close_connection,
+ server, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ }
+ }
+ }
+
+ if (server->config->clients) {
+ SilcServerConfigClient *ptr;
+ SilcServerConfigClient *newptr;
+ bool found;
+
+ for (ptr = server->config->clients; ptr; ptr = ptr->next) {
+ found = FALSE;
+
+ /* Check whether new config has this one too */
+ for (newptr = newconfig->clients; newptr; newptr = newptr->next) {
+ if (silc_string_compare(newptr->host, ptr->host)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found && ptr->host) {
+ /* Remove this connection */
+ SilcSocketConnection sock;
+ sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_CLIENT,
+ ptr->host, 0);
+ if (sock)
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rehash_close_connection,
+ server, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ }
+ }
+ }
+
/* Go through all configured routers after rehash */
silc_schedule_task_add(server->schedule, 0,
silc_server_connect_to_router,
void silc_server_stop(SilcServer server)
{
- SILC_LOG_DEBUG(("Stopping server"));
+ SILC_LOG_INFO(("SILC Server shutting down"));
if (server->schedule) {
+ int i;
+
+ server->server_shutdown = TRUE;
+
+ /* Close all connections */
+ for (i = 0; i < server->config->param.connections_max; i++) {
+ if (!server->sockets[i])
+ continue;
+ if (!SILC_IS_LISTENER(server->sockets[i])) {
+ SilcSocketConnection sock = server->sockets[i];
+ SilcIDListData idata = sock->user_data;
+
+ if (idata)
+ idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+ silc_schedule_task_del_by_context(server->schedule,
+ server->sockets[i]);
+ silc_server_disconnect_remote(server, server->sockets[i],
+ SILC_STATUS_OK,
+ "Server is shutting down");
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock,
+ "Server is shutting down");
+ silc_socket_free(sock);
+ } else {
+ silc_socket_free(server->sockets[i]);
+ server->sockets[i] = NULL;
+ }
+ }
+
+ /* We are not connected to network anymore */
+ server->standalone = TRUE;
+
silc_schedule_stop(server->schedule);
silc_schedule_uninit(server->schedule);
server->schedule = NULL;
+
+ silc_free(server->sockets);
+ server->sockets = NULL;
}
silc_server_protocols_unregister();
SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
{
+ SilcServer server = app_context;
SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServer server = sconn->server;
SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
SilcServerConfigConnParams *param =
(conn->param ? conn->param : &server->config->param);
+ /* Don't retry if we are shutting down. */
+ if (server->server_shutdown) {
+ silc_server_config_unref(&sconn->conn);
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
+ return;
+ }
+
SILC_LOG_INFO(("Retrying connecting to a router"));
/* Calculate next timeout */
return;
}
+ SILC_LOG_DEBUG(("Retrying connecting to a router in %d seconds",
+ sconn->retry_timeout));
+
/* We will lookup a fresh pointer later */
silc_server_config_unref(&sconn->conn);
SILC_TASK_CALLBACK(silc_server_connect_router)
{
+ SilcServer server = app_context;
SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServer server = sconn->server;
SilcServerConfigRouter *rconn;
int sock;
+ /* Don't connect if we are shutting down. */
+ if (server->server_shutdown) {
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
+ return;
+ }
+
SILC_LOG_INFO(("Connecting to the %s %s on port %d",
(sconn->backup ? "backup router" : "router"),
sconn->remote_host, sconn->remote_port));
silc_server_connect_to_router_retry,
context, 0, 1, SILC_TASK_TIMEOUT,
SILC_TASK_PRI_NORMAL);
- else
+ else {
silc_server_config_unref(&sconn->conn);
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
+ }
return;
}
SilcServerConnection sconn;
SilcServerConfigRouter *ptr;
- SILC_LOG_DEBUG(("Connecting to router(s)"));
+ /* Don't connect if we are shutting down. */
+ if (server->server_shutdown)
+ return;
- if (server->server_type == SILC_SERVER) {
- SILC_LOG_DEBUG(("We are normal server"));
- } else if (server->server_type == SILC_ROUTER) {
- SILC_LOG_DEBUG(("We are router"));
- } else {
- SILC_LOG_DEBUG(("We are backup router/normal server"));
- }
+ SILC_LOG_DEBUG(("We are %s",
+ (server->server_type == SILC_SERVER ?
+ "normal server" : server->server_type == SILC_ROUTER ?
+ "router" : "backup router/normal server")));
if (!server->config->routers) {
/* There wasn't a configured router, we will continue but we don't
have a connection to outside world. We will be standalone server. */
- SILC_LOG_DEBUG(("No router(s), server will be standalone"));
+ SILC_LOG_DEBUG(("No router(s), we are standalone"));
server->standalone = TRUE;
return;
}
ptr->initiator ? "Initiator" : "Responder",
ptr->host, ptr->port));
+ if (server->server_type == SILC_ROUTER && ptr->backup_router &&
+ ptr->initiator == FALSE && !server->backup_router &&
+ !silc_server_config_get_backup_router(server))
+ server->wait_backup = TRUE;
+
if (ptr->initiator) {
/* Check whether we are connected to this host already */
if (silc_server_num_sockets_by_remote(server,
/* Allocate connection object for hold connection specific stuff. */
sconn = silc_calloc(1, sizeof(*sconn));
- sconn->server = server;
sconn->remote_host = strdup(ptr->host);
sconn->remote_port = ptr->port;
sconn->backup = ptr->backup_router;
silc_ske_free(ctx->ske);
silc_free(ctx->dest_id);
silc_free(ctx);
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_to_router_retry,
+ sconn, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
+ }
+
+ /* Call completion to indicate error */
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+
+ silc_server_config_unref(&sconn->conn);
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
return;
}
silc_ske_free(ctx->ske);
silc_free(ctx->dest_id);
silc_free(ctx);
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_to_router_retry,
+ sconn, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
+ }
+
+ /* Call completion to indicate error */
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+
+ silc_server_config_unref(&sconn->conn);
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
return;
}
silc_ske_free_key_material(ctx->keymat);
SilcServer server = (SilcServer)ctx->server;
SilcServerConnection sconn = (SilcServerConnection)ctx->context;
SilcSocketConnection sock = ctx->sock;
- SilcServerEntry id_entry;
+ SilcServerEntry id_entry = NULL;
SilcBuffer packet;
- SilcServerHBContext hb_context;
unsigned char *id_string;
SilcUInt32 id_len;
SilcIDListData idata;
silc_free(ctx->dest_id);
silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
NULL);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_to_router_retry,
+ sconn, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ goto out2;
+ }
+
goto out;
}
SILC_LOG_DEBUG(("New server id(%s)",
silc_id_render(ctx->dest_id, SILC_ID_SERVER)));
- /* Add the connected router to global server list */
+ /* Add the connected router to global server list. Router is sent
+ as NULL since it's local to us. */
id_entry = silc_idlist_add_server(server->global_list,
strdup(sock->hostname),
SILC_ROUTER, ctx->dest_id, NULL, sock);
sock->user_data = (void *)id_entry;
sock->type = SILC_SOCKET_TYPE_ROUTER;
idata = (SilcIDListData)sock->user_data;
- idata->status |= SILC_IDLIST_STATUS_REGISTERED;
+ idata->status |= (SILC_IDLIST_STATUS_REGISTERED |
+ SILC_IDLIST_STATUS_LOCAL);
conn = sconn->conn.ref_ptr;
param = &server->config->param;
if (conn && conn->param)
param = conn->param;
- /* Perform keepalive. The `hb_context' will be freed automatically
- when finally calling the silc_socket_free function. */
- hb_context = silc_calloc(1, sizeof(*hb_context));
- hb_context->server = server;
- silc_socket_set_heartbeat(sock, param->keepalive_secs, hb_context,
+ /* Perform keepalive. */
+ silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
silc_server_perform_heartbeat,
server->schedule);
/* Register re-key timeout */
idata->rekey->timeout = param->key_exchange_rekey;
- idata->rekey->context = (void *)server;
silc_schedule_task_add(server->schedule, sock->sock,
silc_server_rekey_callback,
(void *)sock, idata->rekey->timeout, 0,
if (!sconn->backup) {
/* Mark this router our primary router if we're still standalone */
if (server->standalone) {
+ SILC_LOG_DEBUG(("This connection is our primary router"));
server->id_entry->router = id_entry;
server->router = id_entry;
server->standalone = FALSE;
- /* If we are router then announce our possible servers. */
+ /* If we are router then announce our possible servers. Backup
+ router announces also global servers. */
if (server->server_type == SILC_ROUTER)
- silc_server_announce_servers(server, FALSE, 0,
- server->router->connection);
+ silc_server_announce_servers(server,
+ server->backup_router ? TRUE : FALSE,
+ 0, SILC_PRIMARY_ROUTE(server));
/* Announce our clients and channels to the router */
- silc_server_announce_clients(server, 0, server->router->connection);
- silc_server_announce_channels(server, 0, server->router->connection);
+ silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
+ silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
-#ifdef BACKUP_SINGLE_ROUTER
/* 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);
-#endif /* BACKUP_SINGLE_ROUTER */
+ silc_server_backup_add(server, server->id_entry, sock->ip,
+ sconn->remote_port, TRUE);
}
} else {
/* Add this server to be our backup router */
sock->protocol = NULL;
+ out:
/* Call the completion callback to indicate that we've connected to
the router */
- if (sconn->callback)
+ if (sconn && sconn->callback)
(*sconn->callback)(server, id_entry, sconn->callback_context);
- out:
/* Free the temporary connection data context */
if (sconn) {
silc_server_config_unref(&sconn->conn);
if (sconn == server->router_conn)
server->router_conn = NULL;
+ out2:
/* Free the protocol object */
if (sock->protocol == protocol)
sock->protocol = NULL;
SilcServerConfigDeny *deny;
int port;
- context = (void *)server;
-
- SILC_LOG_DEBUG(("Start"));
-
/* Check whether we could resolve both IP and FQDN. */
if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
server->config->require_reverse_lookup)) {
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,
sock->ip));
/* Listenning port */
+ if (!server->sockets[(SilcUInt32)proto_ctx->context]) {
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT,
+ "Connection refused");
+ server->stat.conn_failures++;
+ silc_free(proto_ctx);
+ return;
+ }
port = server->sockets[(SilcUInt32)proto_ctx->context]->port;
/* Check whether this connection is denied to connect to us. */
initiator of the protocol thus we will wait for initiation from
there before we start the protocol. */
server->stat.auth_attempts++;
+ SILC_LOG_DEBUG(("Starting key exchange protocol"));
silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
&sock->protocol, proto_ctx,
silc_server_accept_new_connection_second);
if ((protocol->state == SILC_PROTOCOL_STATE_ERROR) ||
(protocol->state == SILC_PROTOCOL_STATE_FAILURE)) {
/* Error occured during protocol */
+ SILC_LOG_DEBUG(("Error key exchange protocol"));
silc_protocol_free(protocol);
sock->protocol = NULL;
silc_ske_free_key_material(ctx->keymat);
ctx->ske->prop->hmac,
ctx->ske->prop->group,
ctx->responder)) {
+ SILC_LOG_ERROR(("Error setting key material in use"));
silc_protocol_free(protocol);
sock->protocol = NULL;
silc_ske_free_key_material(ctx->keymat);
but we won't start it yet. We will be receiving party of this
protocol thus we will wait that connecting party will make
their first move. */
+ SILC_LOG_DEBUG(("Starting connection authentication protocol"));
silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
&sock->protocol, proto_ctx,
silc_server_accept_new_connection_final);
SILC_TASK_PRI_LOW);
}
+/* After this is called, server don't wait for backup router anymore.
+ This gets called automatically even after we have backup router
+ connection established. */
+
+SILC_TASK_CALLBACK(silc_server_backup_router_wait)
+{
+ SilcServer server = context;
+ server->wait_backup = FALSE;
+}
+
/* Final part of accepting new connection. The connection has now
been authenticated and keys has been exchanged. We also know whether
this is client or server connection. */
(SilcServerConnAuthInternalContext *)protocol->context;
SilcServer server = (SilcServer)ctx->server;
SilcSocketConnection sock = ctx->sock;
- SilcServerHBContext hb_context;
SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data;
void *id_entry;
- SilcUInt32 hearbeat_timeout = server->config->param.keepalive_secs;
-
- SILC_LOG_DEBUG(("Start"));
+ SilcServerConfigConnParams *param = &server->config->param;
if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
+ SILC_LOG_DEBUG(("Error during authentication protocol"));
silc_protocol_free(protocol);
sock->protocol = NULL;
if (ctx->packet)
goto out;
}
+ /* If we are primary router and we have backup router configured
+ but it has not connected to use yet, do not accept any other
+ connection. */
+ if (server->wait_backup && server->server_type == SILC_ROUTER &&
+ !server->backup_router) {
+ SilcServerConfigRouter *router;
+ router = silc_server_config_get_backup_router(server);
+ if (router && strcmp(server->config->server_info->primary->server_ip,
+ sock->ip) &&
+ silc_server_find_socket_by_host(server,
+ SILC_SOCKET_TYPE_SERVER,
+ router->backup_replace_ip, 0)) {
+ SILC_LOG_INFO(("Will not accept connections because we do "
+ "not have backup router connection established"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_PERM_DENIED,
+ "We do not have connection to backup "
+ "router established, try later");
+ silc_free(sock->user_data);
+ server->stat.auth_failures++;
+
+ /* From here on, wait 10 seconds for the backup router to appear. */
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_backup_router_wait,
+ (void *)server, 10, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ goto out;
+ }
+ }
+
SILC_LOG_DEBUG(("Remote host is client"));
SILC_LOG_INFO(("Connection %s (%s) is client", sock->hostname,
sock->ip));
server->stat.auth_failures++;
goto out;
}
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
/* Statistics */
server->stat.my_clients++;
/* Get connection parameters */
if (conn->param) {
- if (conn->param->keepalive_secs)
- hearbeat_timeout = conn->param->keepalive_secs;
+ param = conn->param;
+
+ if (!param->keepalive_secs)
+ param->keepalive_secs = server->config->param.keepalive_secs;
+
+ if (!param->qos && server->config->param.qos) {
+ param->qos = server->config->param.qos;
+ param->qos_rate_limit = server->config->param.qos_rate_limit;
+ param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+ param->qos_limit_sec = server->config->param.qos_limit_sec;
+ param->qos_limit_usec = server->config->param.qos_limit_usec;
+ }
+
+ /* Check if to be anonymous connection */
+ if (param->anonymous)
+ client->mode |= SILC_UMODE_ANONYMOUS;
}
id_entry = (void *)client;
SilcServerConfigServer *sconn = ctx->sconfig.ref_ptr;
SilcServerConfigRouter *rconn = ctx->rconfig.ref_ptr;
+ /* If we are backup router and this is incoming server connection
+ and we do not have connection to primary router, do not allow
+ the connection. */
+ if (server->server_type == SILC_BACKUP_ROUTER &&
+ ctx->conn_type == SILC_SOCKET_TYPE_SERVER &&
+ !SILC_PRIMARY_ROUTE(server)) {
+ SILC_LOG_INFO(("Will not accept server connection because we do "
+ "not have primary router connection established"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_PERM_DENIED,
+ "We do not have connection to primary "
+ "router established, try later");
+ silc_free(sock->user_data);
+ server->stat.auth_failures++;
+ goto out;
+ }
+
if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
/* Verify whether this connection is after all allowed to connect */
if (!silc_server_connection_allowed(server, sock, ctx->conn_type,
&server->config->param,
rconn ? rconn->param : NULL,
ctx->ske)) {
+ silc_free(sock->user_data);
server->stat.auth_failures++;
goto out;
}
if (rconn) {
if (rconn->param) {
- if (rconn->param->keepalive_secs)
- hearbeat_timeout = rconn->param->keepalive_secs;
+ param = rconn->param;
+
+ if (!param->keepalive_secs)
+ param->keepalive_secs = server->config->param.keepalive_secs;
+
+ if (!param->qos && server->config->param.qos) {
+ param->qos = server->config->param.qos;
+ param->qos_rate_limit = server->config->param.qos_rate_limit;
+ param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+ param->qos_limit_sec = server->config->param.qos_limit_sec;
+ param->qos_limit_usec = server->config->param.qos_limit_usec;
+ }
}
initiator = rconn->initiator;
&server->config->param,
sconn ? sconn->param : NULL,
ctx->ske)) {
+ silc_free(sock->user_data);
server->stat.auth_failures++;
goto out;
}
if (sconn) {
if (sconn->param) {
- if (sconn->param->keepalive_secs)
- hearbeat_timeout = sconn->param->keepalive_secs;
+ param = sconn->param;
+
+ if (!param->keepalive_secs)
+ param->keepalive_secs = server->config->param.keepalive_secs;
+
+ if (!param->qos && server->config->param.qos) {
+ param->qos = server->config->param.qos;
+ param->qos_rate_limit = server->config->param.qos_rate_limit;
+ param->qos_bytes_limit = server->config->param.qos_bytes_limit;
+ param->qos_limit_sec = server->config->param.qos_limit_sec;
+ param->qos_limit_usec = server->config->param.qos_limit_usec;
+ }
}
backup_router = sconn->backup_router;
}
}
+ /* If we are primary router and we have backup router configured
+ but it has not connected to use yet, do not accept any other
+ connection. */
+ if (server->wait_backup && server->server_type == SILC_ROUTER &&
+ !server->backup_router && !backup_router) {
+ SilcServerConfigRouter *router;
+ router = silc_server_config_get_backup_router(server);
+ if (router && strcmp(server->config->server_info->primary->server_ip,
+ sock->ip) &&
+ silc_server_find_socket_by_host(server,
+ SILC_SOCKET_TYPE_SERVER,
+ router->backup_replace_ip, 0)) {
+ SILC_LOG_INFO(("Will not accept connections because we do "
+ "not have backup router connection established"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_PERM_DENIED,
+ "We do not have connection to backup "
+ "router established, try later");
+ silc_free(sock->user_data);
+ server->stat.auth_failures++;
+
+ /* From here on, wait 10 seconds for the backup router to appear. */
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_backup_router_wait,
+ (void *)server, 10, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ goto out;
+ }
+ }
+
SILC_LOG_DEBUG(("Remote host is %s",
ctx->conn_type == SILC_SOCKET_TYPE_SERVER ?
"server" : (backup_router ?
server->stat.auth_failures++;
goto out;
}
-
- /* Statistics */
- if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER) {
- server->stat.my_servers++;
- } else {
- server->stat.my_routers++;
- server->stat.routers++;
- }
- server->stat.servers++;
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
id_entry = (void *)new_server;
/* If the incoming connection is router and marked as backup router
then add it to be one of our backups */
if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && backup_router) {
- silc_server_backup_add(server, new_server, backup_replace_ip,
- backup_replace_port, backup_local);
-
/* Change it back to SERVER type since that's what it really is. */
if (backup_local)
ctx->conn_type = SILC_SOCKET_TYPE_SERVER;
-
new_server->server_type = SILC_BACKUP_ROUTER;
+
+ SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+ ("Backup router %s is now online",
+ sock->hostname));
+
+ /* Remove the backup waiting with timeout */
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_backup_router_wait,
+ (void *)server, 5, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ }
+
+ /* Statistics */
+ if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER) {
+ server->stat.my_servers++;
+ } else {
+ server->stat.my_routers++;
+ server->stat.routers++;
}
+ server->stat.servers++;
/* Check whether this connection is to be our primary router connection
if we do not already have the primary route. */
/* Connection has been fully established now. Everything is ok. */
SILC_LOG_DEBUG(("New connection authenticated"));
- /* Perform keepalive. The `hb_context' will be freed automatically
- when finally calling the silc_socket_free function. */
- hb_context = silc_calloc(1, sizeof(*hb_context));
- hb_context->server = server;
- silc_socket_set_heartbeat(sock, hearbeat_timeout, hb_context,
- silc_server_perform_heartbeat,
- server->schedule);
+ /* Perform keepalive. */
+ if (param->keepalive_secs)
+ silc_socket_set_heartbeat(sock, param->keepalive_secs, server,
+ silc_server_perform_heartbeat,
+ server->schedule);
+
+ /* Perform Quality of Service */
+ if (param->qos)
+ silc_socket_set_qos(sock, param->qos_rate_limit, param->qos_bytes_limit,
+ param->qos_limit_sec, param->qos_limit_usec,
+ server->schedule);
out:
silc_schedule_task_del_by_callback(server->schedule,
SilcUInt32 sequence = 0;
int ret;
- if (!sock)
+ if (!sock) {
+ SILC_LOG_DEBUG(("Unknown socket connection"));
return;
-
- SILC_LOG_DEBUG(("Processing packet"));
+ }
/* Packet sending */
if (type == SILC_TASK_WRITE) {
/* Do not send data to disconnected connection */
- if (SILC_IS_DISCONNECTED(sock))
+ if (SILC_IS_DISCONNECTED(sock)) {
+ SILC_LOG_DEBUG(("Disconnected socket connection, cannot send"));
return;
+ }
server->stat.packets_sent++;
if (ret == -2)
return;
+ /* The packet has been sent and now it is time to set the connection
+ back to only for input. When there is again some outgoing data
+ available for this connection it will be set for output as well.
+ This call clears the output setting and sets it only for input. */
+ SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
+
if (ret == -1) {
SILC_LOG_ERROR(("Error sending packet to connection "
"%s:%d [%s]", sock->hostname, sock->port,
sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
"Router")));
- return;
- }
- /* The packet has been sent and now it is time to set the connection
- back to only for input. When there is again some outgoing data
- available for this connection it will be set for output as well.
- This call clears the output setting and sets it only for input. */
- SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
- SILC_UNSET_OUTBUF_PENDING(sock);
-
- silc_buffer_clear(sock->outbuf);
+ SILC_SET_DISCONNECTING(sock);
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_close_connection(server, sock);
+ }
return;
}
ret = silc_packet_receive(sock);
if (ret < 0) {
- if (ret == -1)
+ if (ret == -1) {
SILC_LOG_ERROR(("Error receiving packet from connection "
"%s:%d [%s] %s", sock->hostname, sock->port,
(sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
"Router"), strerror(errno)));
+
+ SILC_SET_DISCONNECTING(sock);
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_close_connection(server, sock);
+ }
return;
}
return;
}
- server->stat.packets_received++;
-
/* Get keys and stuff from ID entry */
idata = (SilcIDListData)sock->user_data;
if (idata) {
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);
SilcIDListData idata = (SilcIDListData)sock->user_data;
int ret;
- SILC_LOG_DEBUG(("Start"));
+ server->stat.packets_received++;
/* Parse the packet */
if (parse_ctx->normal)
ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL);
/* If entry is disabled ignore what we got. */
- if (ret != SILC_PACKET_RESUME_ROUTER &&
- idata && idata->status & SILC_IDLIST_STATUS_DISABLED) {
+ if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED &&
+ ret != SILC_PACKET_HEARTBEAT && ret != SILC_PACKET_RESUME_ROUTER &&
+ ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE) {
SILC_LOG_DEBUG(("Connection is disabled"));
goto out;
}
- if (ret == SILC_PACKET_NONE)
+ if (ret == SILC_PACKET_NONE) {
+ SILC_LOG_DEBUG(("Error parsing packet"));
goto out;
+ }
/* Check that the the current client ID is same as in the client's packet. */
if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
SilcClientEntry client = (SilcClientEntry)sock->user_data;
- if (client && client->id) {
+ if (client && client->id && packet->src_id) {
void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
packet->src_id_type);
if (!id || !SILC_ID_CLIENT_COMPARE(client->id, id)) {
silc_free(id);
+ SILC_LOG_DEBUG(("Packet source is not same as sender"));
goto out;
}
silc_free(id);
if (server->server_type == SILC_ROUTER) {
/* Route the packet if it is not destined to us. Other ID types but
server are handled separately after processing them. */
- if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
+ if (packet->dst_id && !(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
packet->dst_id_type == SILC_ID_SERVER &&
sock->type != SILC_SOCKET_TYPE_CLIENT &&
memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
/* Parse the incoming packet type */
silc_server_packet_parse_type(server, sock, packet);
- if (server->server_type == SILC_ROUTER) {
- /* Broadcast packet if it is marked as broadcast packet and it is
- originated from router and we are router. */
- if (sock->type == SILC_SOCKET_TYPE_ROUTER &&
- packet->flags & SILC_PACKET_FLAG_BROADCAST &&
- !server->standalone) {
- /* Broadcast to our primary route */
- silc_server_packet_broadcast(server, server->router->connection, packet);
-
- /* If we have backup routers then we need to feed all broadcast
- data to those servers. */
- silc_server_backup_broadcast(server, sock, packet);
- }
+ /* Broadcast packet if it is marked as broadcast packet and it is
+ originated from router and we are router. */
+ if (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER &&
+ packet->flags & SILC_PACKET_FLAG_BROADCAST) {
+ /* Broadcast to our primary route */
+ silc_server_packet_broadcast(server, SILC_PRIMARY_ROUTE(server), packet);
+
+ /* If we have backup routers then we need to feed all broadcast
+ data to those servers. */
+ silc_server_backup_broadcast(server, sock, packet);
}
out:
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;
(sock->protocol && sock->protocol->protocol &&
(sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
- silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+ silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
parser_context);
/* Reprocess data since we'll return FALSE here. This is because
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;
}
case SILC_SOCKET_TYPE_SERVER:
case SILC_SOCKET_TYPE_ROUTER:
/* Packets from servers are parsed immediately */
- silc_server_packet_parse_real(server->schedule, 0, sock->sock,
+ silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
parser_context);
break;
default:
SilcPacketType type = packet->type;
SilcIDListData idata = (SilcIDListData)sock->user_data;
- SILC_LOG_DEBUG(("Parsing packet type %d", type));
+ SILC_LOG_DEBUG(("Received %s packet [flags %d]",
+ silc_get_packet_name(type), packet->flags));
/* Parse the packet type */
switch (type) {
SilcStatus status;
char *message = NULL;
- SILC_LOG_DEBUG(("Disconnect packet"));
-
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
if (packet->buffer->len < 1)
status = (SilcStatus)packet->buffer->data[0];
if (packet->buffer->len > 1 &&
silc_utf8_valid(packet->buffer->data + 1, packet->buffer->len - 1))
- message = silc_memdup(packet->buffer->data, packet->buffer->len);
+ message = silc_memdup(packet->buffer->data + 1,
+ packet->buffer->len - 1);
- SILC_LOG_ERROR(("Disconnected by %s (%s): %s (%d) %s",
- sock->ip, sock->hostname,
- silc_get_status_message(status), status,
- message ? message : ""));
+ SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s",
+ sock->ip, sock->hostname,
+ silc_get_status_message(status), status,
+ message ? message : ""));
silc_free(message);
+
+ /* Handle the disconnection from our end too */
+ if (sock->user_data && SILC_IS_LOCAL(sock->user_data))
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_close_connection(server, sock);
}
break;
* one protocol for connection executing at once hence this
* success message is for whatever protocol is executing currently.
*/
- SILC_LOG_DEBUG(("Success packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
if (sock->protocol)
* one protocol for connection executing at once hence this
* failure message is for whatever protocol is executing currently.
*/
- SILC_LOG_DEBUG(("Failure packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
if (sock->protocol) {
SilcServerFailureContext f;
f = silc_calloc(1, sizeof(*f));
- f->server = server;
- f->sock = sock;
+ f->sock = silc_socket_dup(sock);
/* We will wait 5 seconds to process this failure packet */
silc_schedule_task_add(server->schedule, sock->sock,
- silc_server_failure_callback, (void *)f, 5, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_server_failure_callback, (void *)f, 5, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
break;
case SILC_PACKET_REJECT:
- SILC_LOG_DEBUG(("Reject packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
return;
* Received notify packet. Server can receive notify packets from
* router. Server then relays the notify messages to clients if needed.
*/
- SILC_LOG_DEBUG(("Notify packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
silc_server_notify_list(server, sock, packet);
else
* (although probably most common ones) thus they are handled
* specially.
*/
- SILC_LOG_DEBUG(("Channel Message packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
idata->last_receive = time(NULL);
* locally connected clients on the particular channel. Router
* never receives this channel and thus is ignored.
*/
- SILC_LOG_DEBUG(("Channel Key packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_channel_key(server, sock, packet);
* Recived command. Processes the command request and allocates the
* command context and calls the command.
*/
- SILC_LOG_DEBUG(("Command packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_command_process(server, sock, packet);
* may be reply to command sent by us or reply to command sent by client
* that we've routed further.
*/
- SILC_LOG_DEBUG(("Command Reply packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_command_reply(server, sock, packet);
* Received private message packet. The packet is coming from either
* client or server.
*/
- SILC_LOG_DEBUG(("Private Message packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
idata->last_receive = time(NULL);
* Key Exchange protocol packets
*/
case SILC_PACKET_KEY_EXCHANGE:
- SILC_LOG_DEBUG(("KE packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
break;
case SILC_PACKET_KEY_EXCHANGE_1:
- SILC_LOG_DEBUG(("KE 1 packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
break;
case SILC_PACKET_KEY_EXCHANGE_2:
- SILC_LOG_DEBUG(("KE 2 packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
* authentication method for the connection. This packet maybe received
* at any time.
*/
- SILC_LOG_DEBUG(("Connection authentication request packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_connection_auth_request(server, sock, packet);
case SILC_PACKET_CONNECTION_AUTH:
/* Start of the authentication protocol. We receive here the
authentication data and will verify it. */
- SILC_LOG_DEBUG(("Connection auth packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
* to distribute information about new registered entities in the
* SILC network.
*/
- SILC_LOG_DEBUG(("New ID packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
silc_server_new_id_list(server, sock, packet);
else
* we will use to create initial client ID. After creating new
* ID we will send it to the client.
*/
- SILC_LOG_DEBUG(("New Client packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_new_client(server, sock, packet);
* information that we may save. This is received after server has
* connected to us.
*/
- SILC_LOG_DEBUG(("New Server packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_new_server(server, sock, packet);
* Received new channel packet. Information about new channel in the
* network are distributed using this packet.
*/
- SILC_LOG_DEBUG(("New Channel packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
silc_server_new_channel_list(server, sock, packet);
else
/*
* Received heartbeat.
*/
- SILC_LOG_DEBUG(("Heartbeat packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
break;
/*
* Received heartbeat.
*/
- SILC_LOG_DEBUG(("Key agreement packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_key_agreement(server, sock, packet);
* Received re-key packet. The sender wants to regenerate the session
* keys.
*/
- SILC_LOG_DEBUG(("Re-key packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_rekey(server, sock, packet);
/*
* The re-key is done.
*/
- SILC_LOG_DEBUG(("Re-key done packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
case SILC_PACKET_FTP:
/* FTP packet */
- SILC_LOG_DEBUG(("FTP packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_ftp(server, sock, packet);
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);
case SILC_PACKET_RESUME_ROUTER:
/* Resume router packet received. This packet is received for backup
router resuming protocol. */
- SILC_LOG_DEBUG(("Resume router packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
silc_server_backup_resume_router(server, sock, packet);
SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
break;
}
-
}
/* Creates connection to a remote router. */
/* Allocate connection object for hold connection specific stuff. */
sconn = silc_calloc(1, sizeof(*sconn));
- sconn->server = server;
sconn->remote_host = strdup(remote_host);
sconn->remote_port = port;
sconn->no_reconnect = TRUE;
SILC_TASK_CALLBACK(silc_server_close_connection_final)
{
- silc_socket_free((SilcSocketConnection)context);
+ silc_socket_free(context);
}
/* Closes connection to socket connection */
void silc_server_close_connection(SilcServer server,
SilcSocketConnection sock)
{
- if (!server->sockets[sock->sock])
+ char tmp[128];
+
+ if (!server->sockets[sock->sock] && SILC_IS_DISCONNECTED(sock)) {
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_close_connection_final,
+ (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
return;
+ }
- SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname,
+ memset(tmp, 0, sizeof(tmp));
+ silc_socket_get_error(sock, tmp, sizeof(tmp));
+ SILC_LOG_INFO(("Closing connection %s:%d [%s] %s", sock->hostname,
sock->port,
(sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
- "Router")));
+ "Router"), tmp[0] ? tmp : ""));
/* We won't listen for this connection anymore */
silc_schedule_unset_listen_fd(server->schedule, sock->sock);
/* If any protocol is active cancel its execution. It will call
the final callback which will finalize the disconnection. */
if (sock->protocol) {
+ SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
silc_protocol_cancel(sock->protocol, server->schedule);
sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
silc_protocol_execute_final(sock->protocol, server->schedule);
char *cp;
int len;
- if (!sock)
+ if (!sock || SILC_IS_DISCONNECTED(sock))
return;
memset(buf, 0, sizeof(buf));
silc_server_close_connection(server, sock);
}
-typedef struct {
- SilcServer server;
- SilcClientEntry client;
-} *FreeClientInternal;
-
SILC_TASK_CALLBACK(silc_server_free_client_data_timeout)
{
- FreeClientInternal i = (FreeClientInternal)context;
+ SilcServer server = app_context;
+ SilcClientEntry client = context;
- silc_idlist_del_data(i->client);
- silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
- silc_free(i);
+ assert(!silc_hash_table_count(client->channels));
+
+ silc_idlist_del_data(client);
+ silc_idcache_purge_by_context(server->local_list->clients, client);
}
/* Frees client data and notifies about client's signoff. */
int notify,
const char *signoff)
{
- FreeClientInternal i = silc_calloc(1, sizeof(*i));
+ SILC_LOG_DEBUG(("Freeing client data"));
/* If there is pending outgoing data for the client then purge it
to the network before removing the client entry. */
SILC_NOTIFY_TYPE_SIGNOFF);
/* 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, signoff);
-
- /* Remove client from all channels */
if (notify)
- silc_server_remove_from_channels(server, NULL, client,
- TRUE, (char *)signoff, TRUE);
- else
- silc_server_remove_from_channels(server, NULL, client,
- FALSE, NULL, FALSE);
-
- /* Remove this client from watcher list if it is */
- silc_server_del_from_watcher_list(server, client);
+ silc_server_send_notify_signoff(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), client->id,
+ signoff);
}
+ /* Remove client from all channels */
+ if (notify)
+ silc_server_remove_from_channels(server, NULL, client,
+ TRUE, (char *)signoff, TRUE, FALSE);
+ else
+ silc_server_remove_from_channels(server, NULL, client,
+ FALSE, NULL, FALSE, FALSE);
+
+ /* Remove this client from watcher list if it is */
+ silc_server_del_from_watcher_list(server, client);
+
/* Update statistics */
server->stat.my_clients--;
server->stat.clients--;
silc_schedule_task_del_by_context(server->schedule, client);
/* We will not delete the client entry right away. We will take it
- into history (for WHOWAS command) for 5 minutes */
- i->server = server;
- i->client = client;
- silc_schedule_task_add(server->schedule, 0,
- silc_server_free_client_data_timeout,
- (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;
+ into history (for WHOWAS command) for 5 minutes, unless we're
+ shutting down server. */
+ if (!server->server_shutdown) {
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_free_client_data_timeout,
+ client, 300, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+ client->data.status &= ~SILC_IDLIST_STATUS_LOCAL;
+ client->mode = 0;
+ client->router = NULL;
+ client->connection = NULL;
+ } else {
+ /* Delete directly since we're shutting down server */
+ silc_idlist_del_data(client);
+ silc_idlist_del_client(server->local_list, client);
+ }
}
/* Frees user_data pointer from socket connection object. This also sends
SilcSocketConnection sock,
const char *signoff_message)
{
- SILC_LOG_DEBUG(("Start"));
-
switch (sock->type) {
case SILC_SOCKET_TYPE_CLIENT:
{
SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
SilcServerEntry backup_router = NULL;
+ SILC_LOG_DEBUG(("Freeing server data"));
+
if (user_data->id)
backup_router = silc_server_backup_get(server, user_data->id);
+ if (!server->backup_router && server->server_type == SILC_ROUTER &&
+ backup_router == server->id_entry &&
+ sock->type != SILC_SOCKET_TYPE_ROUTER)
+ backup_router = NULL;
+
+ if (server->server_shutdown || server->backup_noswitch)
+ backup_router = NULL;
+
/* If this was our primary router connection then we're lost to
the outside world. */
if (server->router == user_data) {
server->standalone = TRUE;
backup_router = NULL;
} else {
- SILC_LOG_INFO(("New primary router is backup router %s",
- backup_router->server_name));
- SILC_LOG_DEBUG(("New primary router is backup router %s",
- backup_router->server_name));
-#ifdef BACKUP_SINGLE_ROUTER
if (server->id_entry != backup_router) {
-#endif /* BACKUP_SINGLE_ROUTER */
+ SILC_LOG_INFO(("New primary router is backup router %s",
+ backup_router->server_name));
server->id_entry->router = backup_router;
server->router = backup_router;
server->router_connect = time(0);
server->backup_primary = TRUE;
-#ifdef BACKUP_SINGLE_ROUTER
} else {
+ SILC_LOG_INFO(("We are now new primary router in this cell"));
server->id_entry->router = NULL;
server->router = NULL;
server->standalone = TRUE;
+
+ /* We stop here to take a breath */
+ sleep(2);
}
-#endif /* BACKUP_SINGLE_ROUTER */
if (server->server_type == SILC_BACKUP_ROUTER) {
server->server_type = SILC_ROUTER;
} else if (backup_router) {
SILC_LOG_INFO(("Enabling the use of backup router %s",
backup_router->server_name));
- SILC_LOG_DEBUG(("Enabling the use of backup router %s",
- backup_router->server_name));
/* 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) {
- /* Free all client entries that this server owns as they will
- become invalid now as well. */
- if (user_data->id)
- silc_server_remove_clients_by_server(server, user_data, TRUE);
+ /* Remove all servers that are originated from this server, and
+ remove the clients of those servers too. */
+ silc_server_remove_servers_by_server(server, user_data, TRUE);
+
+ /* Remove the clients that this server owns as they will become
+ invalid now too. For backup router the server is actually
+ coming from the primary router, so mark that as the owner
+ of this entry. */
+ if (server->server_type == SILC_BACKUP_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_SERVER)
+ silc_server_remove_clients_by_server(server, server->router,
+ user_data, TRUE);
+ else
+ silc_server_remove_clients_by_server(server, user_data,
+ user_data, TRUE);
+
+ /* Remove channels owned by this server */
if (server->server_type == SILC_SERVER)
silc_server_remove_channels_by_server(server, user_data);
} else {
+ /* Enable local server connections that may be disabled */
+ silc_server_local_servers_toggle_enabled(server, TRUE);
+
/* Update the client entries of this server to the new backup
- router. This also removes the clients that *really* was owned
- by the primary router and went down with the router. */
- silc_server_update_clients_by_server(server, user_data, backup_router,
- TRUE, TRUE);
+ router. If we are the backup router we also resolve the real
+ servers for the clients. After updating is over this also
+ removes the clients that this server explicitly owns. */
+ silc_server_update_clients_by_server(server, user_data,
+ backup_router, TRUE);
+
+ /* If we are router and just lost our primary router (now standlaone)
+ we remove everything that was behind it, since we don't know
+ any better. */
+ if (server->server_type == SILC_ROUTER && server->standalone)
+ /* Remove all servers that are originated from this server, and
+ remove the clients of those servers too. */
+ silc_server_remove_servers_by_server(server, user_data, TRUE);
+
+ /* Finally remove the clients that are explicitly owned by this
+ server. They go down with the server. */
+ silc_server_remove_clients_by_server(server, user_data,
+ user_data, TRUE);
+
+ /* Update our server cache to use the new backup router too. */
silc_server_update_servers_by_server(server, user_data, backup_router);
if (server->server_type == SILC_SERVER)
silc_server_update_channels_by_server(server, user_data,
backup_router);
+
+ /* Send notify about primary router going down to local operators */
+ if (server->backup_router)
+ SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_NONE,
+ ("%s switched to backup router %s "
+ "(we are primary router now)",
+ server->server_name, server->server_name));
+ else if (server->router)
+ SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_NONE,
+ ("%s switched to backup router %s",
+ server->server_name,
+ server->router->server_name));
}
+ server->backup_noswitch = FALSE;
/* Free the server entry */
silc_server_backup_del(server, user_data);
if (server->server_type == SILC_ROUTER)
server->stat.cell_servers--;
- if (backup_router) {
+ if (backup_router && backup_router != server->id_entry) {
/* Announce all of our stuff that was created about 5 minutes ago.
The backup router knows all the other stuff already. */
if (server->server_type == SILC_ROUTER)
{
SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
+ SILC_LOG_DEBUG(("Freeing unknown connection data"));
+
silc_idlist_del_data(user_data);
silc_free(user_data);
break;
/* If any protocol is active cancel its execution */
if (sock->protocol) {
+ SILC_LOG_DEBUG(("Cancelling protocol, calling final callback"));
silc_protocol_cancel(sock->protocol, server->schedule);
sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
silc_protocol_execute_final(sock->protocol, server->schedule);
SilcClientEntry client,
bool notify,
const char *signoff_message,
- bool keygen)
+ bool keygen,
+ bool killed)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
SilcHashTableList htl;
- SilcBuffer clidp;
-
- SILC_LOG_DEBUG(("Start"));
+ SilcBuffer clidp = NULL;
- if (!client || !client->id)
+ if (!client)
return;
- clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- if (!clidp)
+ SILC_LOG_DEBUG(("Removing client from joined channels"));
+
+ if (notify && !client->id)
notify = FALSE;
+ if (notify) {
+ clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (!clidp)
+ notify = FALSE;
+ }
+
/* Remove the client from all channels. The client is removed from
the channels' user list. */
silc_hash_table_list(client->channels, &htl);
/* Remove channel if this is last client leaving the channel, unless
the channel is permanent. */
- if (server->server_type == SILC_ROUTER &&
+ if (server->server_type != SILC_SERVER &&
silc_hash_table_count(channel->user_list) < 2) {
silc_server_channel_delete(server, channel);
continue;
}
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
as local channel. Do not check if the removed client is local client. */
- if (server->server_type != SILC_ROUTER && channel->global_users &&
+ if (server->server_type == SILC_SERVER && channel->global_users &&
chl->client->router && !silc_server_channel_has_global(channel))
channel->global_users = FALSE;
+ memset(chl, 'A', sizeof(*chl));
silc_free(chl);
/* Update statistics */
- if (client->connection)
+ if (SILC_IS_LOCAL(client))
server->stat.my_chanclients--;
if (server->server_type == SILC_ROUTER) {
server->stat.cell_chanclients--;
/* If there is not at least one local user on the channel then we don't
need the channel entry anymore, we can remove it safely, unless the
channel is permanent channel */
- if (server->server_type != SILC_ROUTER &&
+ if (server->server_type == SILC_SERVER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
if (notify && channel->global_users)
signoff_message, signoff_message ?
strlen(signoff_message) : 0);
+ if (killed && clidp) {
+ /* Remove the client from channel's invite list */
+ if (channel->invite_list &&
+ silc_hash_table_count(channel->invite_list)) {
+ SilcBuffer ab;
+ SilcArgumentPayload iargs;
+ ab = silc_argument_payload_encode_one(NULL, clidp->data,
+ clidp->len, 3);
+ iargs = silc_argument_payload_parse(ab->data, ab->len, 1);
+ silc_server_inviteban_process(server, channel->invite_list, 1, iargs);
+ silc_buffer_free(ab);
+ silc_argument_payload_free(iargs);
+ }
+ }
+
+ /* Don't create keys if we are shutting down */
+ if (server->server_shutdown)
+ continue;
+
/* Re-generate channel key if needed */
if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
if (!silc_server_create_channel_key(server, channel, 0))
}
silc_hash_table_list_reset(&htl);
- silc_buffer_free(clidp);
+ if (clidp)
+ silc_buffer_free(clidp);
}
/* Removes client from one channel. This is used for example when client
/* Remove channel if this is last client leaving the channel, unless
the channel is permanent. */
- if (server->server_type == SILC_ROUTER &&
+ if (server->server_type != SILC_SERVER &&
silc_hash_table_count(channel->user_list) < 2) {
silc_server_channel_delete(server, channel);
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
as local channel. Do not check if the client is local client. */
- if (server->server_type != SILC_ROUTER && channel->global_users &&
+ if (server->server_type == SILC_SERVER && channel->global_users &&
chl->client->router && !silc_server_channel_has_global(channel))
channel->global_users = FALSE;
+ memset(chl, 'O', sizeof(*chl));
silc_free(chl);
/* Update statistics */
- if (client->connection)
+ if (SILC_IS_LOCAL(client))
server->stat.my_chanclients--;
if (server->server_type == SILC_ROUTER) {
server->stat.cell_chanclients--;
/* If there is not at least one local user on the channel then we don't
need the channel entry anymore, we can remove it safely, unless the
channel is permanent channel */
- if (server->server_type != SILC_ROUTER &&
+ if (server->server_type == SILC_SERVER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
if (notify && channel->global_users)
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
SilcCipher key;
SilcHmac newhmac;
- SILC_LOG_DEBUG(("Creating new channel"));
+ SILC_LOG_DEBUG(("Creating new channel %s", channel_name));
if (!cipher)
cipher = SILC_DEFAULT_CIPHER;
/* Notify other routers about the new channel. We send the packet
to our primary route. */
- if (broadcast && server->standalone == FALSE)
- silc_server_send_new_channel(server, server->router->connection, TRUE,
+ if (broadcast)
+ silc_server_send_new_channel(server, SILC_PRIMARY_ROUTE(server), TRUE,
channel_name, entry->id,
silc_id_get_len(entry->id, SILC_ID_CHANNEL),
entry->mode);
SilcCipher key;
SilcHmac newhmac;
- SILC_LOG_DEBUG(("Creating new channel"));
+ SILC_LOG_DEBUG(("Creating new channel %s", channel_name));
if (!cipher)
cipher = SILC_DEFAULT_CIPHER;
/* Notify other routers about the new channel. We send the packet
to our primary route. */
- if (broadcast && server->standalone == FALSE)
- silc_server_send_new_channel(server, server->router->connection, TRUE,
+ if (broadcast)
+ silc_server_send_new_channel(server, SILC_PRIMARY_ROUTE(server), TRUE,
channel_name, entry->id,
silc_id_get_len(entry->id, SILC_ID_CHANNEL),
entry->mode);
SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
{
+ SilcServer server = app_context;
SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
- SilcServer server = (SilcServer)rekey->context;
rekey->task = NULL;
+ /* Return now if we are shutting down */
+ if (server->server_shutdown)
+ return;
+
if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len))
return;
unsigned char channel_key[32], hash[32];
SilcUInt32 len;
- SILC_LOG_DEBUG(("Generating channel key"));
-
if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
return TRUE;
}
+ SILC_LOG_DEBUG(("Generating channel %s key", channel->channel_name));
+
if (!channel->channel_key)
if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key)) {
channel->channel_key = NULL;
if (server->server_type == SILC_ROUTER) {
if (!channel->rekey)
channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
- channel->rekey->context = (void *)server;
channel->rekey->channel = channel;
channel->rekey->key_len = key_len;
if (channel->rekey->task)
SilcUInt32 tmp_len;
char *cipher;
- SILC_LOG_DEBUG(("Start"));
-
/* Decode channel key payload */
payload = silc_channel_key_payload_parse(key_payload->data,
key_payload->len);
if (!channel) {
channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
if (!channel) {
- SILC_LOG_ERROR(("Received key for non-existent channel %s",
- silc_id_render(id, SILC_ID_CHANNEL)));
+ if (server->server_type == SILC_ROUTER)
+ SILC_LOG_ERROR(("Received key for non-existent channel %s",
+ silc_id_render(id, SILC_ID_CHANNEL)));
goto out;
}
}
}
+ SILC_LOG_DEBUG(("Saving new channel %s key", channel->channel_name));
+
tmp = silc_channel_key_get_key(payload, &tmp_len);
if (!tmp) {
channel = NULL;
if (server->server_type == SILC_ROUTER) {
if (!channel->rekey)
channel->rekey = silc_calloc(1, sizeof(*channel->rekey));
- channel->rekey->context = (void *)server;
channel->rekey->channel = channel;
if (channel->rekey->task)
silc_schedule_task_del(server->schedule, channel->rekey->task);
void silc_server_perform_heartbeat(SilcSocketConnection sock,
void *hb_context)
{
- SilcServerHBContext hb = (SilcServerHBContext)hb_context;
+ SilcServer server = hb_context;
- SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip));
+ SILC_LOG_DEBUG(("Sending heartbeat to %s:%d (%s)", sock->hostname,
+ sock->port, sock->ip));
/* Send the heartbeat */
- silc_server_send_heartbeat(hb->server, sock);
+ silc_server_send_heartbeat(server, sock);
}
/* Returns assembled of all servers in the given ID list. The packet's
break;
continue;
}
+ if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) &&
+ !client->connection && !client->router && !SILC_IS_LOCAL(client)) {
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ continue;
+ }
idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
silc_buffer_pull(*clients, idp->len);
SILC_PUT32_MSB(client->mode, mode);
- tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
- 2, idp->data, idp->len,
- mode, 4);
+ tmp =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+ 2, idp->data, idp->len,
+ mode, 4);
*umodes = silc_buffer_realloc(*umodes,
(*umodes ?
(*umodes)->truelen + tmp->len :
SilcChannelClientEntry chl;
SilcHashTableList htl;
SilcBuffer chidp, clidp, csidp;
- SilcBuffer tmp;
+ SilcBuffer tmp, fkey = NULL;
int len;
- unsigned char mode[4], *fkey = NULL;
- SilcUInt32 fkey_len = 0;
+ unsigned char mode[4];
char *hmac;
SILC_LOG_DEBUG(("Start"));
SILC_PUT32_MSB(channel->mode, mode);
hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
if (channel->founder_key)
- fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
+ fkey = silc_pkcs_public_key_payload_encode(channel->founder_key);
tmp =
silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
6, csidp->data, csidp->len,
channel->passphrase,
channel->passphrase ?
strlen(channel->passphrase) : 0,
- fkey, fkey_len);
+ fkey ? fkey->data : NULL,
+ fkey ? fkey->len : 0);
len = tmp->len;
*channel_modes =
silc_buffer_realloc(*channel_modes,
silc_buffer_put(*channel_modes, tmp->data, tmp->len);
silc_buffer_pull(*channel_modes, len);
silc_buffer_free(tmp);
- silc_free(fkey);
+ silc_buffer_free(fkey);
fkey = NULL;
- fkey_len = 0;
/* Now find all users on the channel */
silc_hash_table_list(channel->user_list, &htl);
/* CUMODE notify for mode change on the channel */
SILC_PUT32_MSB(chl->mode, mode);
if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && channel->founder_key)
- fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
+ fkey = silc_pkcs_public_key_payload_encode(channel->founder_key);
tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
4, csidp->data, csidp->len,
mode, sizeof(mode),
clidp->data, clidp->len,
- fkey, fkey_len);
+ fkey ? fkey->data : NULL,
+ fkey ? fkey->len : 0);
len = tmp->len;
*channel_users_modes =
silc_buffer_realloc(*channel_users_modes,
silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
silc_buffer_pull(*channel_users_modes, len);
silc_buffer_free(tmp);
- silc_free(fkey);
+ silc_buffer_free(fkey);
fkey = NULL;
- fkey_len = 0;
silc_buffer_free(clidp);
}
silc_hash_table_list_reset(&htl);
silc_buffer_pull(*channels, len);
}
- /* Channel user modes */
- *channel_users_modes = silc_realloc(*channel_users_modes,
- sizeof(**channel_users_modes) *
- (i + 1));
- (*channel_users_modes)[i] = NULL;
- *channel_modes = silc_realloc(*channel_modes,
- sizeof(**channel_modes) * (i + 1));
- (*channel_modes)[i] = NULL;
- *channel_ids = silc_realloc(*channel_ids,
- sizeof(**channel_ids) * (i + 1));
- (*channel_ids)[i] = NULL;
- silc_server_announce_get_channel_users(server, channel,
- &(*channel_modes)[i],
- channel_users,
- &(*channel_users_modes)[i]);
- (*channel_ids)[i] = channel->id;
-
- /* Channel's topic */
- *channel_topics = silc_realloc(*channel_topics,
- sizeof(**channel_topics) * (i + 1));
- (*channel_topics)[i] = NULL;
- silc_server_announce_get_channel_topic(server, channel,
- &(*channel_topics)[i]);
- i++;
+ if (creation_time && channel->updated < creation_time)
+ announce = FALSE;
+ else
+ announce = TRUE;
+
+ if (announce) {
+ /* Channel user modes */
+ *channel_users_modes = silc_realloc(*channel_users_modes,
+ sizeof(**channel_users_modes) *
+ (i + 1));
+ (*channel_users_modes)[i] = NULL;
+ *channel_modes = silc_realloc(*channel_modes,
+ sizeof(**channel_modes) * (i + 1));
+ (*channel_modes)[i] = NULL;
+ *channel_ids = silc_realloc(*channel_ids,
+ sizeof(**channel_ids) * (i + 1));
+ (*channel_ids)[i] = NULL;
+ silc_server_announce_get_channel_users(server, channel,
+ &(*channel_modes)[i],
+ channel_users,
+ &(*channel_users_modes)[i]);
+ (*channel_ids)[i] = channel->id;
+
+ /* Channel's topic */
+ *channel_topics = silc_realloc(*channel_topics,
+ sizeof(**channel_topics) * (i + 1));
+ (*channel_topics)[i] = NULL;
+ silc_server_announce_get_channel_topic(server, channel,
+ &(*channel_topics)[i]);
+ (*channel_users_modes_c)++;
+
+ silc_free(cid);
+
+ i++;
+ }
if (!silc_idcache_list_next(list, &id_cache))
break;
}
-
- *channel_users_modes_c += i;
}
silc_idcache_list_free(list);
silc_buffer_free(channels);
}
+ if (channel_users) {
+ silc_buffer_push(channel_users, channel_users->data - channel_users->head);
+ SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
+ channel_users->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_users->data, channel_users->len,
+ FALSE);
+
+ silc_buffer_free(channel_users);
+ }
+
if (channel_modes) {
int i;
silc_free(channel_modes);
}
- if (channel_users) {
- silc_buffer_push(channel_users, channel_users->data - channel_users->head);
- SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
- channel_users->len);
-
- /* Send the packet */
- silc_server_packet_send(server, remote,
- SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
- channel_users->data, channel_users->len,
- FALSE);
-
- silc_buffer_free(channel_users);
- }
-
if (channel_users_modes) {
int i;
SILC_TASK_CALLBACK(silc_server_failure_callback)
{
+ SilcServer server = app_context;
SilcServerFailureContext f = (SilcServerFailureContext)context;
if (f->sock->protocol) {
f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0);
+ silc_protocol_execute(f->sock->protocol, server->schedule, 0, 0);
}
+ silc_socket_free(f->sock);
silc_free(f);
}
SilcClientEntry client;
SilcIDCacheEntry cache;
SilcChannelClientEntry chl;
- bool global;
- SILC_LOG_DEBUG(("Start"));
+ SILC_LOG_DEBUG(("Saving %d users on %s channel", user_count,
+ channel->channel_name));
for (i = 0; i < user_count; i++) {
/* Client ID */
continue;
}
- global = FALSE;
+ cache = NULL;
/* Check if we have this client cached already. */
client = silc_idlist_find_client_by_id(server->local_list, client_id,
server->server_type, &cache);
- if (!client) {
+ if (!client)
client = silc_idlist_find_client_by_id(server->global_list,
client_id, server->server_type,
&cache);
- global = TRUE;
- }
if (!client) {
/* If router did not find such Client ID in its lists then this must
be bogus client or some router in the net is buggy. */
- if (server->server_type == SILC_ROUTER) {
+ if (server->server_type != SILC_SERVER) {
silc_free(client_id);
continue;
}
}
client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
- } else {
- /* Found, if it is from global list we'll assure that we won't
- expire it now that the entry is on channel. */
- if (global)
- cache->expire = 0;
}
+ if (cache)
+ cache->expire = 0;
silc_free(client_id);
+ if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
+ SILC_LOG_ERROR(("Attempting to add unregistered client to channel ",
+ "%s", channel->channel_name));
+ continue;
+ }
+
if (!silc_server_client_on_channel(client, channel, &chl)) {
/* Client was not on the channel, add it. */
chl = silc_calloc(1, sizeof(*chl));
char *name;
int i = 0;
- if (!channels ||!channels_user_modes)
+ if (!channels || !channels_user_modes ||
+ !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
goto out;
ch = silc_channel_payload_parse_list(channels->data, channels->len);
silc_free(id);
if (idata)
*idata = (SilcIDListData)server->router;
- return server->router->connection;
+ return SILC_PRIMARY_ROUTE(server);
}
/* We are router and we will perform route lookup for the destination
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 :
- server->router->connection,
- 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. */
-SILC_TASK_CALLBACK(silc_server_rekey_callback)
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_callback)
{
+ SilcServer server = app_context;
SilcSocketConnection sock = (SilcSocketConnection)context;
SilcIDListData idata = (SilcIDListData)sock->user_data;
- SilcServer server = (SilcServer)idata->rekey->context;
SilcProtocol protocol;
SilcServerRekeyInternalContext *proto_ctx;
- SILC_LOG_DEBUG(("Start"));
-
/* Allocate internal protocol context. This is sent as context
to the protocol. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
/* Run the protocol */
silc_protocol_execute(protocol, server->schedule, 0, 0);
+ SILC_LOG_DEBUG(("Rekey protocol completed"));
+
/* Re-register re-key timeout */
silc_schedule_task_add(server->schedule, sock->sock,
silc_server_rekey_callback,
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_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
SILC_PACKET_COMMAND, 0, packet->data,
packet->len, FALSE);
silc_buffer_free(packet);