server.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2001 Pekka Riikonen
+ Copyright (C) 1997 - 2008 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- 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
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
-/*
- * This is the actual SILC server than handles everything relating to
- * servicing the SILC connections. This is also a SILC router as a router
- * is also normal server.
- */
-/* $Id$ */
#include "serverincludes.h"
#include "server_internal.h"
-/* Static prototypes */
-SILC_TASK_CALLBACK(silc_server_connect_router);
-SILC_TASK_CALLBACK(silc_server_connect_to_router);
-SILC_TASK_CALLBACK(silc_server_connect_to_router_second);
-SILC_TASK_CALLBACK(silc_server_connect_to_router_final);
-SILC_TASK_CALLBACK(silc_server_accept_new_connection);
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_second);
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_final);
-SILC_TASK_CALLBACK(silc_server_packet_process);
-SILC_TASK_CALLBACK(silc_server_packet_parse_real);
-SILC_TASK_CALLBACK(silc_server_timeout_remote);
-SILC_TASK_CALLBACK(silc_server_failure_callback);
-
-/* 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
- the server. The new allocated server object is returned to the new_server
- argument. */
+/************************* Types and definitions ****************************/
-int silc_server_alloc(SilcServer *new_server)
+SILC_TASK_CALLBACK(silc_server_get_stats);
+SILC_TASK_CALLBACK(silc_server_connect_router);
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry);
+SILC_TASK_CALLBACK(silc_server_do_rekey);
+SILC_TASK_CALLBACK(silc_server_purge_expired_clients);
+static void silc_server_accept_new_connection(SilcNetStatus status,
+ SilcStream stream,
+ void *context);
+static void silc_server_packet_parse_type(SilcServer server,
+ SilcPacketStream sock,
+ SilcPacket packet);
+static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
+ SilcPacket packet);
+
+
+/************************ Static utility functions **************************/
+
+/* SKE public key verification callback */
+
+static void
+silc_server_verify_key(SilcSKE ske,
+ SilcPublicKey public_key,
+ void *context,
+ SilcSKEVerifyCbCompletion completion,
+ void *completion_context)
{
- SilcServer server;
-
- SILC_LOG_DEBUG(("Allocating new server object"));
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
- server = silc_calloc(1, sizeof(*server));
- server->server_type = SILC_SERVER;
- server->standalone = TRUE;
- server->local_list = silc_calloc(1, sizeof(*server->local_list));
- server->global_list = silc_calloc(1, sizeof(*server->global_list));
- server->pending_commands = silc_dlist_init();
-#ifdef SILC_SIM
- server->sim = silc_dlist_init();
-#endif
+ SILC_LOG_DEBUG(("Verifying public key"));
- *new_server = server;
+ if (silc_pkcs_get_type(public_key) != SILC_SKE_PK_TYPE_SILC) {
+ SILC_LOG_WARNING(("We don't support %s (%s) port %d public key type %d",
+ entry->hostname, entry->ip, entry->port,
+ silc_pkcs_get_type(public_key)));
+ completion(ske, SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
+ completion_context);
+ return;
+ }
- return TRUE;
+ /* We accept all keys without explicit verification */
+ completion(ske, SILC_SKE_STATUS_OK, completion_context);
}
-/* Free's the SILC server object. This is called at the very end before
- the program ends. */
-
-void silc_server_free(SilcServer server)
-{
- if (server) {
-#ifdef SILC_SIM
- SilcSimContext *sim;
-#endif
-
- if (server->local_list)
- silc_free(server->local_list);
- if (server->global_list)
- silc_free(server->global_list);
- if (server->rng)
- silc_rng_free(server->rng);
-
-#ifdef SILC_SIM
- while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
- silc_dlist_del(server->sim, sim);
- silc_sim_free(sim);
- }
- silc_dlist_uninit(server->sim);
-#endif
-
- if (server->params)
- silc_free(server->params);
- if (server->pending_commands)
- silc_dlist_uninit(server->pending_commands);
-
- silc_free(server);
- }
-}
+/************************ Packet engine callbacks ***************************/
-/* Initializes the entire SILC server. This is called always before running
- the server. This is called only once at the initialization of the program.
- This binds the server to its listenning port. After this function returns
- one should call silc_server_run to start the server. This returns TRUE
- when everything is ok to run the server. Configuration file must be
- read and parsed before calling this. */
+/* Packet engine callback to receive a packet */
-int silc_server_init(SilcServer server)
+static SilcBool silc_server_packet_receive(SilcPacketEngine engine,
+ SilcPacketStream stream,
+ SilcPacket packet,
+ void *callback_context,
+ void *stream_context)
{
- int *sock = NULL, sock_count = 0, i;
- SilcServerID *id;
- SilcServerEntry id_entry;
- SilcIDListPurge purge;
-
- SILC_LOG_DEBUG(("Initializing server"));
- assert(server);
- assert(server->config);
-
- /* XXX After server is made as Silc Server Library this can be given
- as argument, for now this is hard coded */
- server->params = silc_calloc(1, sizeof(*server->params));
- server->params->retry_count = SILC_SERVER_RETRY_COUNT;
- server->params->retry_interval_min = SILC_SERVER_RETRY_INTERVAL_MIN;
- server->params->retry_interval_max = SILC_SERVER_RETRY_INTERVAL_MAX;
- server->params->retry_keep_trying = FALSE;
- server->params->protocol_timeout = 60;
- server->params->require_reverse_mapping = FALSE;
-
- /* Set log files where log message should be saved. */
- server->config->server = server;
- silc_server_config_setlogfiles(server->config);
-
- /* Register all configured ciphers, PKCS and hash functions. */
- silc_server_config_register_ciphers(server->config);
- silc_server_config_register_pkcs(server->config);
- silc_server_config_register_hashfuncs(server->config);
- silc_server_config_register_hmacs(server->config);
+ SilcServer server = callback_context;
+ SilcIDListData idata = stream_context;
- /* Initialize random number generator for the server. */
- server->rng = silc_rng_alloc();
- silc_rng_init(server->rng);
- silc_rng_global_init(server->rng);
+ if (!idata)
+ return FALSE;
- /* Initialize hash functions for server to use */
- silc_hash_alloc("md5", &server->md5hash);
- silc_hash_alloc("sha1", &server->sha1hash);
+ /* Packets we do not handle */
+ switch (packet->type) {
+ case SILC_PACKET_HEARTBEAT:
+ case SILC_PACKET_SUCCESS:
+ case SILC_PACKET_FAILURE:
+ case SILC_PACKET_REJECT:
+ case SILC_PACKET_KEY_EXCHANGE:
+ case SILC_PACKET_KEY_EXCHANGE_1:
+ case SILC_PACKET_KEY_EXCHANGE_2:
+ case SILC_PACKET_REKEY_DONE:
+ case SILC_PACKET_CONNECTION_AUTH:
+ return FALSE;
+ break;
+ }
- /* Initialize none cipher */
- silc_cipher_alloc("none", &server->none_cipher);
+ /* Only specific packets can come without source ID present. */
+ if ((!packet->src_id ||
+ !(idata->status & SILC_IDLIST_STATUS_REGISTERED)) &&
+ packet->type != SILC_PACKET_NEW_CLIENT &&
+ packet->type != SILC_PACKET_NEW_SERVER &&
+ packet->type != SILC_PACKET_RESUME_CLIENT &&
+ packet->type != SILC_PACKET_CONNECTION_AUTH_REQUEST &&
+ packet->type != SILC_PACKET_DISCONNECT)
+ return FALSE;
- /* XXXXX Generate RSA key pair */
- {
- unsigned char *public_key;
- unsigned char *private_key;
- uint32 pk_len, prv_len;
- struct stat st;
+ /* NEW_CLIENT and NEW_SERVER are accepted only without source ID and
+ for unregistered connection. */
+ if (packet->src_id && (packet->type == SILC_PACKET_NEW_CLIENT ||
+ packet->type == SILC_PACKET_NEW_SERVER) &&
+ (idata->status & SILC_IDLIST_STATUS_REGISTERED))
+ return FALSE;
- if (stat("pubkey.pub", &st) < 0 && stat("privkey.prv", &st) < 0) {
+ /* Ignore packets from disabled connection */
+ if (idata->status & SILC_IDLIST_STATUS_DISABLED &&
+ packet->type != SILC_PACKET_HEARTBEAT &&
+ packet->type != SILC_PACKET_RESUME_ROUTER &&
+ packet->type != SILC_PACKET_REKEY)
+ return FALSE;
- if (silc_pkcs_alloc("rsa", &server->pkcs) == FALSE) {
- SILC_LOG_ERROR(("Could not create RSA key pair"));
- goto err0;
- }
-
- if (server->pkcs->pkcs->init(server->pkcs->context,
- 1024, server->rng) == FALSE) {
- SILC_LOG_ERROR(("Could not generate RSA key pair"));
- goto err0;
+ /* Check that the the current client ID is same as in the client's packet. */
+ if (idata->conn_type == SILC_CONN_CLIENT) {
+ SilcClientEntry client = (SilcClientEntry)silc_packet_get_context(stream);
+ SilcClientID client_id;
+
+ if (client->id && packet->src_id &&
+ silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type, &client_id, sizeof(client_id))) {
+ if (!SILC_ID_CLIENT_COMPARE(client->id, &client_id)) {
+ SILC_LOG_DEBUG(("Packet source is not same as sender, packet %s",
+ silc_get_packet_name(packet->type)));
+ return FALSE;
}
-
- public_key = server->pkcs->pkcs->get_public_key(server->pkcs->context,
- &pk_len);
- private_key = server->pkcs->pkcs->get_private_key(server->pkcs->context,
- &prv_len);
-
- SILC_LOG_HEXDUMP(("public key"), public_key, pk_len);
- SILC_LOG_HEXDUMP(("private key"), private_key, prv_len);
-
- server->public_key =
- silc_pkcs_public_key_alloc("rsa", "UN=root, HN=dummy",
- public_key, pk_len);
- server->private_key =
- silc_pkcs_private_key_alloc("rsa", private_key, prv_len);
-
- /* XXX Save keys */
- silc_pkcs_save_public_key("pubkey.pub", server->public_key,
- SILC_PKCS_FILE_PEM);
- silc_pkcs_save_private_key("privkey.prv", server->private_key, NULL,
- SILC_PKCS_FILE_BIN);
-
- memset(public_key, 0, pk_len);
- memset(private_key, 0, prv_len);
- silc_free(public_key);
- silc_free(private_key);
- } else {
- silc_pkcs_load_public_key("pubkey.pub", &server->public_key,
- SILC_PKCS_FILE_PEM);
- silc_pkcs_load_private_key("privkey.prv", &server->private_key,
- SILC_PKCS_FILE_BIN);
}
}
- /* Create a listening server. Note that our server can listen on
- multiple ports. All listeners are created here and now. */
- /* XXX Still check this whether to use server_info or listen_port. */
- sock_count = 0;
- while(server->config->listen_port) {
- int tmp;
-
- tmp = silc_net_create_server(server->config->listen_port->port,
- server->config->listen_port->host);
- if (tmp < 0)
- goto err0;
-
- sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
- sock[sock_count] = tmp;
- server->config->listen_port = server->config->listen_port->next;
- sock_count++;
- }
-
- /* Initialize ID caches */
- server->local_list->clients =
- silc_idcache_alloc(0, silc_idlist_client_destructor);
- server->local_list->servers = silc_idcache_alloc(0, NULL);
- server->local_list->channels = silc_idcache_alloc(0, NULL);
+ 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->dst_id &&
+ !(packet->flags & SILC_PACKET_FLAG_BROADCAST) &&
+ packet->dst_id_type == SILC_ID_SERVER &&
+ idata->conn_type != SILC_CONN_CLIENT &&
+ memcmp(packet->dst_id, server->id_string, server->id_string_len)) {
+ SilcPacketStream conn;
+ SilcServerID server_id;
+
+ silc_id_str2id(packet->dst_id, packet->dst_id_len, packet->dst_id_type,
+ &server_id, sizeof(server_id));
+
+ conn = silc_server_route_get(server, &server_id, SILC_ID_SERVER);
+ if (!conn) {
+ SILC_LOG_WARNING(("Packet to unknown server ID %s, dropped (no route)",
+ silc_id_render(&server_id, SILC_ID_SERVER)));
+ return FALSE;
+ }
- /* These are allocated for normal server as well as these hold some
- global information that the server has fetched from its router. For
- router these are used as they are supposed to be used on router. */
- server->global_list->clients =
- silc_idcache_alloc(0, silc_idlist_client_destructor);
- server->global_list->servers = silc_idcache_alloc(0, NULL);
- server->global_list->channels = silc_idcache_alloc(0, NULL);
-
- /* Allocate the entire socket list that is used in server. Eventually
- all connections will have entry in this table (it is a table of
- pointers to the actual object that is allocated individually
- later). */
- server->sockets = silc_calloc(SILC_SERVER_MAX_CONNECTIONS,
- sizeof(*server->sockets));
-
- for (i = 0; i < sock_count; i++) {
- SilcSocketConnection newsocket = NULL;
-
- /* Set socket to non-blocking mode */
- silc_net_set_socket_nonblock(sock[i]);
- server->sock = sock[i];
-
- /* Create a Server ID for the server. */
- silc_id_create_server_id(sock[i], server->rng, &id);
- if (!id) {
- goto err0;
- }
-
- server->id = id;
- server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
- server->id_string_len = silc_id_get_len(SILC_ID_SERVER);
- server->id_type = SILC_ID_SERVER;
- server->server_name = server->config->server_info->server_name;
-
- /* Add ourselves to the server list. We don't have a router yet
- beacuse we haven't established a route yet. It will be done later.
- For now, NULL is sent as router. This allocates new entry to
- the ID list. */
- id_entry =
- silc_idlist_add_server(server->local_list,
- server->config->server_info->server_name,
- server->server_type, server->id, NULL, NULL);
- if (!id_entry) {
- SILC_LOG_ERROR(("Could not add ourselves to cache"));
- goto err0;
+ silc_server_packet_route(server, conn, packet);
+ silc_packet_free(packet);
+ return TRUE;
}
-
- /* 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[i], SILC_SOCKET_TYPE_SERVER, id_entry,
- &newsocket);
-
- server->sockets[sock[i]] = newsocket;
-
- /* Put the allocated socket pointer also to the entry allocated above
- for fast back-referencing to the socket list. */
- id_entry->connection = (void *)server->sockets[sock[i]];
- server->id_entry = id_entry;
}
- /* Register the task queues. In SILC we have by default three task queues.
- One task queue for non-timeout tasks which perform different kind of
- I/O on file descriptors, timeout task queue for timeout tasks, and,
- generic non-timeout task queue whose tasks apply to all connections. */
- silc_task_queue_alloc(&server->io_queue, TRUE);
- if (!server->io_queue) {
- goto err0;
- }
- silc_task_queue_alloc(&server->timeout_queue, TRUE);
- if (!server->timeout_queue) {
- goto err1;
+ /* 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 &&
+ idata->conn_type == SILC_CONN_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, stream, packet);
}
- silc_task_queue_alloc(&server->generic_queue, TRUE);
- if (!server->generic_queue) {
- goto err1;
- }
-
- /* Register protocols */
- silc_server_protocols_register();
-
- /* Initialize the scheduler */
- silc_schedule_init(&server->io_queue, &server->timeout_queue,
- &server->generic_queue,
- SILC_SERVER_MAX_CONNECTIONS);
-
- /* Add the first task to the queue. This is task that is executed by
- timeout. It expires as soon as the caller calls silc_server_run. This
- task performs authentication protocol and key exchange with our
- primary router. */
- silc_task_register(server->timeout_queue, sock[0],
- silc_server_connect_to_router,
- (void *)server, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
-
- /* Add listener task to the queue. This task receives new connections to the
- server. This task remains on the queue until the end of the program. */
- silc_task_register(server->io_queue, sock[0],
- silc_server_accept_new_connection,
- (void *)server, 0, 0,
- SILC_TASK_FD,
- SILC_TASK_PRI_NORMAL);
- server->listenning = TRUE;
- /* If server connections has been configured then we must be router as
- normal server cannot have server connections, only router connections. */
- if (server->config->servers)
- server->server_type = SILC_ROUTER;
+ /* Process packet */
+ silc_server_packet_parse_type(server, stream, packet);
- /* Register the ID Cache purge task. This periodically purges the ID cache
- and removes the expired cache entries. */
-
- /* Clients local list */
- purge = silc_calloc(1, sizeof(*purge));
- purge->cache = server->local_list->clients;
- purge->timeout_queue = server->timeout_queue;
- silc_task_register(purge->timeout_queue, 0,
- silc_idlist_purge,
- (void *)purge, 600, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
-
- /* Clients global list */
- purge = silc_calloc(1, sizeof(*purge));
- purge->cache = server->global_list->clients;
- purge->timeout_queue = server->timeout_queue;
- silc_task_register(purge->timeout_queue, 0,
- silc_idlist_purge,
- (void *)purge, 300, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ return TRUE;
+}
- SILC_LOG_DEBUG(("Server initialized"));
+/* Packet engine callback to indicate end of stream */
- /* We are done here, return succesfully */
- return TRUE;
+static void silc_server_packet_eos(SilcPacketEngine engine,
+ SilcPacketStream stream,
+ void *callback_context,
+ void *stream_context)
+{
+ SilcServer server = callback_context;
+ SilcIDListData idata = silc_packet_get_context(stream);
- silc_task_queue_free(server->timeout_queue);
- err1:
- silc_task_queue_free(server->io_queue);
- err0:
- for (i = 0; i < sock_count; i++)
- silc_net_close_server(sock[i]);
+ SILC_LOG_DEBUG(("End of stream received, sock %p", stream));
- return FALSE;
-}
+ if (!idata)
+ return;
-/* Fork server to background and set gid+uid to non-root.
- Silcd will not run as root, so trying to set either user or group to
- root will cause silcd to exit. */
-
-void silc_server_daemonise(SilcServer server)
-{
- /* Are we executing silcd as root or a regular user? */
- if (geteuid()==0) {
-
- struct passwd *pw;
- struct group *gr;
- char *user, *group;
-
- if (!server->config->identity || !server->config->identity->user ||
- !server->config->identity->group) {
- fprintf(stderr, "Error:"
- "\tSILC server must not be run as root. For the security of your\n"
- "\tsystem it is strongly suggested that you run SILC under dedicated\n"
- "\tuser account. Modify the [Identity] configuration section to run\n"
- "\tthe server as non-root user.\n");
- exit(1);
- }
-
- /* Get the values given for user and group in configuration file */
- user=server->config->identity->user;
- group=server->config->identity->group;
-
- /* Check whether the user/group information is text */
- if (atoi(user)!=0 || atoi(group)!=0) {
- SILC_LOG_DEBUG(("Invalid user and/or group information"));
- SILC_LOG_DEBUG(("User and/or group given as number"));
- fprintf(stderr, "Invalid user and/or group information\n");
- fprintf(stderr, "Please assign them as names, not numbers\n");
- exit(1);
- }
-
- /* Catch the nasty incident of string "0" returning 0 from atoi */
- if (strcmp("0", user)==0 || strcmp("0", group)==0) {
- SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
- fprintf(stderr, "User and/or group configured to 0. Exiting\n");
- exit(1);
- }
-
- pw=getpwnam(user);
- gr=getgrnam(group);
-
- if (!pw) {
- fprintf(stderr, "No such user %s found\n", user);
- exit(1);
- }
-
- if (!gr) {
- fprintf(stderr, "No such group %s found\n", group);
- exit(1);
- }
-
- /* Check whether user and/or group is set to root. If yes, exit
- immediately. Otherwise, setgid and setuid server to user.group */
- if (gr->gr_gid==0 || pw->pw_uid==0) {
- fprintf(stderr, "Error:"
- "\tSILC server must not be run as root. For the security of your\n"
- "\tsystem it is strongly suggested that you run SILC under dedicated\n"
- "\tuser account. Modify the [Identity] configuration section to run\n"
- "\tthe server as non-root user.\n");
- exit(1);
- } else {
- /* Fork server to background, making it a daemon */
- if (fork()) {
- SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
- SILC_LOG_DEBUG(("Forking SILC server to background"));
- exit(0);
- }
- setsid();
-
- SILC_LOG_DEBUG(("Changing to group %s", group));
- if(setgid(gr->gr_gid)==0) {
- SILC_LOG_DEBUG(("Setgid to %s", group));
- } else {
- SILC_LOG_DEBUG(("Setgid to %s failed", group));
- fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
- group);
- exit(1);
- }
- SILC_LOG_DEBUG(("Changing to user nobody"));
- if(setuid(pw->pw_uid)==0) {
- SILC_LOG_DEBUG(("Setuid to %s", user));
- } else {
- SILC_LOG_DEBUG(("Setuid to %s failed", user));
- fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
- user);
- exit(1);
- }
- }
+ if (server->router_conn && server->router_conn->sock == stream &&
+ !server->router && server->standalone) {
+ silc_server_create_connections(server);
+ silc_server_free_sock_user_data(server, stream, NULL);
} else {
- /* Fork server to background, making it a daemon */
- if (fork()) {
- SILC_LOG_DEBUG(("Server started as user"));
- SILC_LOG_DEBUG(("Forking SILC server to background"));
- exit(0);
+ /* If backup disconnected then mark that resuming will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ idata->conn_type == SILC_CONN_SERVER) {
+ SilcServerEntry server_entry = (SilcServerEntry)idata;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
}
- setsid();
+
+ silc_server_free_sock_user_data(server, stream, NULL);
}
-}
-/* Stops the SILC server. This function is used to shutdown the server.
- This is usually called after the scheduler has returned. After stopping
- the server one should call silc_server_free. */
+ silc_server_close_connection(server, stream);
+}
-void silc_server_stop(SilcServer server)
+SILC_TASK_CALLBACK(silc_server_packet_error_timeout)
{
- SILC_LOG_DEBUG(("Stopping server"));
+ SilcServer server = app_context;
+ SilcPacketStream stream = context;
+ SilcIDListData idata = silc_packet_get_context(stream);
- /* Stop the scheduler, although it might be already stopped. This
- doesn't hurt anyone. This removes all the tasks and task queues,
- as well. */
- silc_schedule_stop();
- silc_schedule_uninit();
-
- silc_server_protocols_unregister();
-
- SILC_LOG_DEBUG(("Server stopped"));
-}
+ if (!idata)
+ return;
-/* The heart of the server. This runs the scheduler thus runs the server.
- When this returns the server has been stopped and the program will
- be terminated. */
+ if (server->router_conn && server->router_conn->sock == stream &&
+ !server->router && server->standalone) {
+ silc_server_create_connections(server);
+ } else {
+ /* If backup disconnected then mark that resuming will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ idata->conn_type == SILC_CONN_SERVER) {
+ SilcServerEntry server_entry = (SilcServerEntry)idata;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
+ }
-void silc_server_run(SilcServer server)
-{
- SILC_LOG_DEBUG(("Running server"));
+ silc_server_free_sock_user_data(server, stream, NULL);
+ }
- /* Start the scheduler, the heart of the SILC server. When this returns
- the program will be terminated. */
- silc_schedule();
+ silc_server_close_connection(server, stream);
}
-/* Timeout callback that will be called to retry connecting to remote
- router. This is used by both normal and router server. This will wait
- before retrying the connecting. The timeout is generated by exponential
- backoff algorithm. */
+/* Packet engine callback to indicate error */
-SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+static void silc_server_packet_error(SilcPacketEngine engine,
+ SilcPacketStream stream,
+ SilcPacketError error,
+ void *callback_context,
+ void *stream_context)
{
- SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServer server = sconn->server;
+ SilcServer server = callback_context;
+ SilcIDListData idata = silc_packet_get_context(stream);
+ SilcStream sock = silc_packet_stream_get_stream(stream);
+ const char *ip;
+ SilcUInt16 port;
- SILC_LOG_INFO(("Retrying connecting to a router"));
-
- /* Calculate next timeout */
- if (sconn->retry_count >= 1) {
- sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
- if (sconn->retry_timeout > SILC_SERVER_RETRY_INTERVAL_MAX)
- sconn->retry_timeout = SILC_SERVER_RETRY_INTERVAL_MAX;
- } else {
- sconn->retry_timeout = server->params->retry_interval_min;
- }
- sconn->retry_count++;
- sconn->retry_timeout = sconn->retry_timeout +
- silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
+ SILC_LOG_DEBUG(("Packet error, sock %p", stream));
- /* If we've reached max retry count, give up. */
- if (sconn->retry_count > server->params->retry_count &&
- server->params->retry_keep_trying == FALSE) {
- SILC_LOG_ERROR(("Could not connect to router, giving up"));
+ if (!idata || !sock)
return;
- }
-
- /* Wait one before retrying */
- silc_task_register(server->timeout_queue, fd, silc_server_connect_router,
- context, sconn->retry_timeout,
- server->params->retry_interval_min_usec,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-}
-/* Generic routine to use connect to a router. */
+ if (!silc_socket_stream_get_info(sock, NULL, NULL, &ip, &port))
+ return;
-SILC_TASK_CALLBACK(silc_server_connect_router)
-{
- SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServer server = sconn->server;
- SilcSocketConnection newsocket;
- SilcProtocol protocol;
- SilcServerKEInternalContext *proto_ctx;
- int sock;
+ SILC_LOG_ERROR(("Connection %s:%d [%s]: %s", ip, port,
+ SILC_CONNTYPE_STRING(idata->conn_type),
+ silc_packet_error_string(error)));
- /* Connect to remote host */
- sock = silc_net_create_connection(sconn->remote_port,
- sconn->remote_host);
- if (sock < 0) {
- SILC_LOG_ERROR(("Could not connect to router"));
- silc_task_register(server->timeout_queue, fd,
- silc_server_connect_to_router_retry,
- context, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ if (!silc_packet_stream_is_valid(stream))
return;
- }
- /* Set socket options */
- silc_net_set_socket_nonblock(sock);
- silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
- /* Create socket connection for the connection. Even though we
- know that we are connecting to a router we will mark the socket
- to be unknown connection until we have executed authentication
- protocol. */
- silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
- server->sockets[sock] = newsocket;
- newsocket->hostname = strdup(sconn->remote_host);
- newsocket->ip = strdup(sconn->remote_host);
- newsocket->port = sconn->remote_port;
- sconn->sock = newsocket;
-
- /* Allocate internal protocol context. This is sent as context
- to the protocol. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->server = (void *)server;
- proto_ctx->context = (void *)sconn;
- proto_ctx->sock = newsocket;
- proto_ctx->rng = server->rng;
- proto_ctx->responder = FALSE;
-
- /* Perform key exchange protocol. silc_server_connect_to_router_second
- will be called after the protocol is finished. */
- silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
- &protocol, proto_ctx,
- silc_server_connect_to_router_second);
- newsocket->protocol = protocol;
-
- /* Register a timeout task that will be executed if the protocol
- is not executed within set limit. */
- proto_ctx->timeout_task =
- silc_task_register(server->timeout_queue, sock,
- silc_server_timeout_remote,
- server, server->params->protocol_timeout,
- server->params->protocol_timeout_usec,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
-
- /* Register the connection for network input and output. This sets
- that scheduler will listen for incoming packets for this connection
- and sets that outgoing packets may be sent to this connection as
- well. 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);
-
- /* Run the protocol */
- protocol->execute(server->timeout_queue, 0, protocol, sock, 0, 0);
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_packet_error_timeout,
+ stream, 0, 0);
}
-
-/* This function connects to our primary router or if we are a router this
- establishes all our primary routes. This is called at the start of the
- server to do authentication and key exchange with our router - called
- from schedule. */
-SILC_TASK_CALLBACK(silc_server_connect_to_router)
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_server_stream_cbs =
{
- SilcServer server = (SilcServer)context;
- SilcServerConnection sconn;
+ silc_server_packet_receive,
+ silc_server_packet_eos,
+ silc_server_packet_error
+};
- SILC_LOG_DEBUG(("Connecting to router(s)"));
+/* Parses the packet type and calls what ever routines the packet type
+ requires. This is done for all incoming packets. */
- /* If we are normal SILC server we need to connect to our cell's
- router. */
- if (server->server_type == SILC_SERVER) {
- SILC_LOG_DEBUG(("We are normal server"));
+static void silc_server_packet_parse_type(SilcServer server,
+ SilcPacketStream sock,
+ SilcPacket packet)
+{
+ SilcPacketType type = packet->type;
+ SilcIDListData idata = silc_packet_get_context(sock);
- /* Create connection to the router, if configured. */
- if (server->config->routers) {
+ SILC_LOG_DEBUG(("Received %s packet [flags %d]",
+ silc_get_packet_name(type), packet->flags));
- /* Allocate connection object for hold connection specific stuff. */
- sconn = silc_calloc(1, sizeof(*sconn));
- sconn->server = server;
- sconn->remote_host = strdup(server->config->routers->host);
- sconn->remote_port = server->config->routers->port;
+ /* Parse the packet type */
+ switch (type) {
+ case SILC_PACKET_NOTIFY:
+ /*
+ * Received notify packet. Server can receive notify packets from
+ * router. Server then relays the notify messages to clients if needed.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_notify_list(server, sock, packet);
+ else
+ silc_server_notify(server, sock, packet);
+ break;
- silc_task_register(server->timeout_queue, fd,
- silc_server_connect_router,
- (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- return;
- }
- }
+ /*
+ * Private Message packets
+ */
+ case SILC_PACKET_PRIVATE_MESSAGE:
+ /*
+ * Received private message packet. The packet is coming from either
+ * client or server.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ idata->last_receive = time(NULL);
+ silc_server_private_message(server, sock, packet);
+ break;
- /* If we are a SILC router we need to establish all of our primary
- routes. */
- if (server->server_type == SILC_ROUTER) {
- SilcServerConfigSectionServerConnection *ptr;
+ /*
+ * Channel packets
+ */
+ case SILC_PACKET_CHANNEL_MESSAGE:
+ /*
+ * Received channel message. Channel messages are special packets
+ * (although probably most common ones) thus they are handled
+ * specially.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ idata->last_receive = time(NULL);
+ silc_server_channel_message(server, sock, packet);
+ break;
- SILC_LOG_DEBUG(("We are router"));
+ /*
+ * Command packets
+ */
+ case SILC_PACKET_COMMAND:
+ /*
+ * Recived command. Processes the command request and allocates the
+ * command context and calls the command.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ server->stat.commands_received++;
+ silc_server_command_process(server, sock, packet);
+ break;
- /* Create the connections to all our routes */
- ptr = server->config->routers;
- while (ptr) {
+ case SILC_PACKET_COMMAND_REPLY:
+ /*
+ * Received command reply packet. Received command reply to command. It
+ * may be reply to command sent by us or reply to command sent by client
+ * that we've routed further.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ server->stat.commands_received++;
+ silc_server_command_reply(server, sock, packet);
+ break;
- SILC_LOG_DEBUG(("Router connection [%s] %s:%d",
- ptr->initiator ? "Initiator" : "Responder",
- ptr->host, ptr->port));
+ case SILC_PACKET_DISCONNECT:
+ {
+ SilcStatus status;
+ char *message = NULL;
+ const char *hostname, *ip;
- if (ptr->initiator) {
- /* 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;
-
- silc_task_register(server->timeout_queue, fd,
- silc_server_connect_router,
- (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- }
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ if (silc_buffer_len(&packet->buffer) < 1)
+ break;
- if (!ptr->next)
- return;
+ status = (SilcStatus)packet->buffer.data[0];
+ if (silc_buffer_len(&packet->buffer) > 1 &&
+ silc_utf8_valid(packet->buffer.data + 1,
+ silc_buffer_len(&packet->buffer) - 1))
+ message = silc_memdup(packet->buffer.data + 1,
+ silc_buffer_len(&packet->buffer) - 1);
- ptr = ptr->next;
- }
- }
+ if (!silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, &ip, NULL))
+ break;
- SILC_LOG_DEBUG(("No router(s), server will be standalone"));
-
- /* There wasn't a configured router, we will continue but we don't
- have a connection to outside world. We will be standalone server. */
- server->standalone = TRUE;
-}
+ SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", ip, hostname,
+ silc_get_status_message(status), status,
+ message ? message : ""));
-/* Second part of connecting to router(s). Key exchange protocol has been
- executed and now we will execute authentication protocol. */
+ silc_free(message);
-SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
-{
- SilcProtocol protocol = (SilcProtocol)context;
- SilcServerKEInternalContext *ctx =
- (SilcServerKEInternalContext *)protocol->context;
- SilcServer server = (SilcServer)ctx->server;
- SilcServerConnection sconn = (SilcServerConnection)ctx->context;
- SilcSocketConnection sock = NULL;
- SilcServerConnAuthInternalContext *proto_ctx;
- SilcServerConfigSectionServerConnection *conn = NULL;
+ /* Do not switch to backup in case of error */
+ server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
- SILC_LOG_DEBUG(("Start"));
+ /* If backup disconnected then mark that resuming will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ idata->conn_type == SILC_CONN_SERVER) {
+ SilcServerEntry server_entry = (SilcServerEntry)idata;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
+ }
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
- protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
- /* Error occured during protocol */
- silc_protocol_free(protocol);
- silc_ske_free_key_material(ctx->keymat);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
- return;
- }
-
- /* We now have the key material as the result of the key exchange
- protocol. Take the key material into use. Free the raw key material
- as soon as we've set them into use. */
- if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
- ctx->ske->prop->cipher,
- ctx->ske->prop->pkcs,
- ctx->ske->prop->hash,
- ctx->ske->prop->hmac,
- ctx->responder)) {
- silc_protocol_free(protocol);
- silc_ske_free_key_material(ctx->keymat);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
- return;
- }
- silc_ske_free_key_material(ctx->keymat);
-
- /* Allocate internal context for the authentication protocol. This
- is sent as context for the protocol. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->server = (void *)server;
- proto_ctx->context = (void *)sconn;
- proto_ctx->sock = sock = server->sockets[fd];
- proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */
- proto_ctx->dest_id_type = ctx->dest_id_type;
- proto_ctx->dest_id = ctx->dest_id;
-
- /* Resolve the authentication method used in this connection. Check if
- we find a match from user configured connections */
- conn = silc_server_config_find_router_conn(server->config,
- sock->hostname,
- sock->port);
- if (conn) {
- /* Match found. Use the configured authentication method */
- proto_ctx->auth_meth = conn->auth_meth;
- if (conn->auth_data) {
- proto_ctx->auth_data = strdup(conn->auth_data);
- proto_ctx->auth_data_len = strlen(conn->auth_data);
+ /* Handle the disconnection from our end too */
+ if (SILC_IS_LOCAL(idata))
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_close_connection(server, sock);
+ server->backup_noswitch = FALSE;
}
- } else {
- SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
- sock->hostname, sock->ip, sock->port));
- silc_protocol_free(protocol);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
- return;
- }
+ break;
- /* Free old protocol as it is finished now */
- silc_protocol_free(protocol);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- silc_free(ctx);
- sock->protocol = NULL;
-
- /* Allocate the authentication protocol. This is allocated here
- 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_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
- &sock->protocol, proto_ctx,
- silc_server_connect_to_router_final);
-
- /* Register timeout task. If the protocol is not executed inside
- this timelimit the connection will be terminated. Currently
- this is 15 seconds and is hard coded limit (XXX). */
- proto_ctx->timeout_task =
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_timeout_remote,
- (void *)server, 15, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
-
- /* Run the protocol */
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
-}
-
-/* Finalizes the connection to router. Registers a server task to the
- queue so that we can accept new connections. */
-
-SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
-{
- SilcProtocol protocol = (SilcProtocol)context;
- SilcServerConnAuthInternalContext *ctx =
- (SilcServerConnAuthInternalContext *)protocol->context;
- SilcServer server = (SilcServer)ctx->server;
- SilcServerConnection sconn = (SilcServerConnection)ctx->context;
- SilcSocketConnection sock = ctx->sock;
- SilcServerEntry id_entry;
- SilcBuffer packet;
- SilcServerHBContext hb_context;
- unsigned char *id_string;
-
- SILC_LOG_DEBUG(("Start"));
-
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
- protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
- /* Error occured during protocol */
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
- goto out;
- }
+ case SILC_PACKET_CHANNEL_KEY:
+ /*
+ * Received key for channel. As channels are created by the router
+ * the keys are as well. We will distribute the key to all of our
+ * locally connected clients on the particular channel. Router
+ * never receives this channel and thus is ignored.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_channel_key(server, sock, packet);
+ break;
- /* Add a task to the queue. This task receives new connections to the
- server. This task remains on the queue until the end of the program. */
- if (!server->listenning) {
- silc_task_register(server->io_queue, server->sock,
- silc_server_accept_new_connection,
- (void *)server, 0, 0,
- SILC_TASK_FD,
- SILC_TASK_PRI_NORMAL);
- server->listenning = TRUE;
- }
-
- /* Send NEW_SERVER packet to the router. We will become registered
- to the SILC network after sending this packet. */
- id_string = silc_id_id2str(server->id, SILC_ID_SERVER);
- packet = silc_buffer_alloc(2 + 2 + SILC_ID_SERVER_LEN +
- strlen(server->server_name));
- silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
- silc_buffer_format(packet,
- SILC_STR_UI_SHORT(SILC_ID_SERVER_LEN),
- SILC_STR_UI_XNSTRING(id_string, SILC_ID_SERVER_LEN),
- SILC_STR_UI_SHORT(strlen(server->server_name)),
- SILC_STR_UI_XNSTRING(server->server_name,
- strlen(server->server_name)),
- SILC_STR_END);
-
- /* Send the packet */
- silc_server_packet_send(server, ctx->sock, SILC_PACKET_NEW_SERVER, 0,
- packet->data, packet->len, TRUE);
- silc_buffer_free(packet);
- silc_free(id_string);
-
- SILC_LOG_DEBUG(("Connected to router %s", sock->hostname));
-
- /* Add the connected router to local server list */
- server->standalone = FALSE;
- id_entry = silc_idlist_add_server(server->local_list, sock->hostname,
- SILC_ROUTER, ctx->dest_id, NULL, sock);
- if (!id_entry) {
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
- goto out;
- }
+ case SILC_PACKET_PRIVATE_MESSAGE_KEY:
+ /*
+ * Private message key packet.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_private_message_key(server, sock, packet);
+ break;
- silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
- silc_free(sock->user_data);
- sock->user_data = (void *)id_entry;
- sock->type = SILC_SOCKET_TYPE_ROUTER;
- server->id_entry->router = id_entry;
- server->router = id_entry;
- server->router->data.registered = TRUE;
-
- /* Perform keepalive. The `hb_context' will be freed automatically
- when finally calling the silc_socket_free function. XXX hardcoded
- timeout!! */
- hb_context = silc_calloc(1, sizeof(*hb_context));
- hb_context->server = server;
- silc_socket_set_heartbeat(sock, 600, hb_context,
- silc_server_perform_heartbeat,
- server->timeout_queue);
-
- /* If we are router then announce our possible servers. */
- if (server->server_type == SILC_ROUTER)
- silc_server_announce_servers(server);
+ case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+ /*
+ * Connection authentication request packet. When we receive this packet
+ * we will send to the other end information about our mandatory
+ * authentication method for the connection. This packet maybe received
+ * at any time.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_connection_auth_request(server, sock, packet);
+ break;
- /* Announce our clients and channels to the router */
- silc_server_announce_clients(server);
- silc_server_announce_channels(server);
+ case SILC_PACKET_NEW_ID:
+ /*
+ * Received New ID packet. This includes some new ID that has been
+ * created. It may be for client, server or channel. This is the way
+ * to distribute information about new registered entities in the
+ * SILC network.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_new_id_list(server, sock, packet);
+ else
+ silc_server_new_id(server, sock, packet);
+ break;
- out:
- /* Free the temporary connection data context */
- if (sconn) {
- silc_free(sconn->remote_host);
- silc_free(sconn);
- }
-
- /* Free the protocol object */
- silc_protocol_free(protocol);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- silc_free(ctx);
- sock->protocol = NULL;
-}
+ case SILC_PACKET_NEW_CLIENT:
+ /*
+ * Received new client packet. This includes client information that
+ * we will use to create initial client ID. After creating new
+ * ID we will send it to the client.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_new_client(server, sock, packet);
+ break;
-/* Accepts new connections to the server. Accepting new connections are
- done in three parts to make it async. */
+ case SILC_PACKET_NEW_SERVER:
+ /*
+ * Received new server packet. This includes Server ID and some other
+ * information that we may save. This is received after server has
+ * connected to us.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_new_server(server, sock, packet);
+ break;
-SILC_TASK_CALLBACK(silc_server_accept_new_connection)
-{
- SilcServer server = (SilcServer)context;
- SilcSocketConnection newsocket;
- SilcServerKEInternalContext *proto_ctx;
- int sock;
+ case SILC_PACKET_NEW_CHANNEL:
+ /*
+ * Received new channel packet. Information about new channel in the
+ * network are distributed using this packet.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_new_channel_list(server, sock, packet);
+ else
+ silc_server_new_channel(server, sock, packet);
+ break;
- SILC_LOG_DEBUG(("Accepting new connection"));
+ case SILC_PACKET_HEARTBEAT:
+ /*
+ * Received heartbeat.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ break;
- server->stat.conn_attempts++;
+ case SILC_PACKET_KEY_AGREEMENT:
+ /*
+ * Received heartbeat.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_key_agreement(server, sock, packet);
+ break;
- sock = silc_net_accept_connection(server->sock);
- if (sock < 0) {
- SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
- server->stat.conn_failures++;
- return;
- }
+ case SILC_PACKET_REKEY:
+ /*
+ * Received re-key packet. The sender wants to regenerate the session
+ * keys.
+ */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_rekey(server, sock, packet);
+ break;
- /* Check max connections */
- if (sock > SILC_SERVER_MAX_CONNECTIONS) {
- SILC_LOG_ERROR(("Refusing connection, server is full"));
- server->stat.conn_failures++;
- return;
- }
+ case SILC_PACKET_FTP:
+ /* FTP packet */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_ftp(server, sock, packet);
+ break;
- /* Set socket options */
- silc_net_set_socket_nonblock(sock);
- silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+ case SILC_PACKET_RESUME_CLIENT:
+ /* Resume client */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_resume_client(server, sock, packet);
+ break;
- /* We don't create a ID yet, since we don't know what type of connection
- this is yet. But, we do add the connection to the socket table. */
- silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
- server->sockets[sock] = newsocket;
+ case SILC_PACKET_RESUME_ROUTER:
+ /* Resume router packet received. This packet is received for backup
+ router resuming protocol. */
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_backup_resume_router(server, sock, packet);
+ break;
- /* XXX This MUST be done async as this will block the entire server
- process. Either we have to do our own resolver stuff or in the future
- we can use threads. */
- /* Perform name and address lookups for the remote host. */
- silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
- if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
- !newsocket->ip) {
- SILC_LOG_ERROR(("IP/DNS lookup failed"));
- server->stat.conn_failures++;
- return;
+ default:
+ SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
+ break;
}
- if (!newsocket->hostname)
- newsocket->hostname = strdup(newsocket->ip);
- newsocket->port = silc_net_get_remote_port(sock);
-
- SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
- newsocket->ip));
-
- /* Allocate internal context for key exchange protocol. This is
- sent as context for the protocol. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->server = context;
- proto_ctx->sock = newsocket;
- proto_ctx->rng = server->rng;
- proto_ctx->responder = TRUE;
-
- /* Prepare the connection for key exchange protocol. We allocate the
- protocol but will not start it yet. The connector will be the
- initiator of the protocol thus we will wait for initiation from
- there before we start the protocol. */
- server->stat.auth_attempts++;
- silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
- &newsocket->protocol, proto_ctx,
- silc_server_accept_new_connection_second);
-
- /* Register a timeout task that will be executed if the connector
- will not start the key exchange protocol within 60 seconds. For
- now, this is a hard coded limit. After 60 secs the connection will
- be closed if the key exchange protocol has not been started. */
- proto_ctx->timeout_task =
- silc_task_register(server->timeout_queue, newsocket->sock,
- silc_server_timeout_remote,
- context, 60, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
-
- /* Register the connection for network input and output. This sets
- that scheduler will listen for incoming packets for this connection
- and sets that outgoing packets may be sent to this connection as well.
- 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. */
- SILC_REGISTER_CONNECTION_FOR_IO(sock);
}
-/* Second part of accepting new connection. Key exchange protocol has been
- performed and now it is time to do little connection authentication
- protocol to figure out whether this connection is client or server
- and whether it has right to access this server (especially server
- connections needs to be authenticated). */
+/****************************** Server API **********************************/
+
+/* 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
+ the server. The new allocated server object is returned to the new_server
+ argument. */
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_second)
+SilcBool silc_server_alloc(SilcServer *new_server)
{
- SilcProtocol protocol = (SilcProtocol)context;
- SilcServerKEInternalContext *ctx =
- (SilcServerKEInternalContext *)protocol->context;
- SilcServer server = (SilcServer)ctx->server;
- SilcSocketConnection sock = NULL;
- SilcServerConnAuthInternalContext *proto_ctx;
+ SilcServer server;
- SILC_LOG_DEBUG(("Start"));
+ SILC_LOG_DEBUG(("Allocating new server object"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
- protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
- /* Error occured during protocol */
- silc_protocol_free(protocol);
- silc_ske_free_key_material(ctx->keymat);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
- server->stat.auth_failures++;
- return;
- }
+ server = silc_calloc(1, sizeof(*server));
+ if (!server)
+ return FALSE;
+ server->server_type = SILC_SERVER;
+ server->standalone = TRUE;
+ server->local_list = silc_calloc(1, sizeof(*server->local_list));
+ if (!server->local_list)
+ return FALSE;
+ server->global_list = silc_calloc(1, sizeof(*server->global_list));
+ if (!server->global_list)
+ return FALSE;
+ server->pending_commands = silc_dlist_init();
+ if (!server->pending_commands)
+ return FALSE;
+ server->listeners = silc_dlist_init();
+ if (!server->listeners)
+ return FALSE;
+ server->repository = silc_skr_alloc();
+ if (!server->repository)
+ return FALSE;
+ server->conns = silc_dlist_init();
+ if (!server->conns)
+ return FALSE;
+ server->expired_clients = silc_dlist_init();
+ if (!server->expired_clients)
+ return FALSE;
- /* We now have the key material as the result of the key exchange
- protocol. Take the key material into use. Free the raw key material
- as soon as we've set them into use. */
- if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
- ctx->ske->prop->cipher,
- ctx->ske->prop->pkcs,
- ctx->ske->prop->hash,
- ctx->ske->prop->hmac,
- ctx->responder)) {
- silc_protocol_free(protocol);
- silc_ske_free_key_material(ctx->keymat);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
- server->stat.auth_failures++;
- return;
- }
- silc_ske_free_key_material(ctx->keymat);
-
- /* Allocate internal context for the authentication protocol. This
- is sent as context for the protocol. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->server = (void *)server;
- proto_ctx->sock = sock = server->sockets[fd];
- proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */
- proto_ctx->responder = TRUE;
- proto_ctx->dest_id_type = ctx->dest_id_type;
- proto_ctx->dest_id = ctx->dest_id;
-
- /* Free old protocol as it is finished now */
- silc_protocol_free(protocol);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- silc_free(ctx);
- sock->protocol = NULL;
-
- /* Allocate the authentication protocol. This is allocated here
- 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_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
- &sock->protocol, proto_ctx,
- silc_server_accept_new_connection_final);
-
- /* Register timeout task. If the protocol is not executed inside
- this timelimit the connection will be terminated. Currently
- this is 60 seconds and is hard coded limit (XXX). */
- proto_ctx->timeout_task =
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_timeout_remote,
- (void *)server, 60, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
+ *new_server = server;
+
+ return TRUE;
}
-/* 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. */
+/* Free's the SILC server object. This is called at the very end before
+ the program ends. */
-SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
+void silc_server_free(SilcServer server)
{
- SilcProtocol protocol = (SilcProtocol)context;
- SilcServerConnAuthInternalContext *ctx =
- (SilcServerConnAuthInternalContext *)protocol->context;
- SilcServer server = (SilcServer)ctx->server;
- SilcSocketConnection sock = ctx->sock;
- SilcServerHBContext hb_context;
- void *id_entry = NULL;
+ SilcList list;
+ SilcIDCacheEntry cache;
+ SilcIDListData idata;
- SILC_LOG_DEBUG(("Start"));
+ SILC_LOG_DEBUG(("Free server %p", server));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
- protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
- /* Error occured during protocol */
- silc_protocol_free(protocol);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_free(ctx);
- if (sock)
- sock->protocol = NULL;
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
- server->stat.auth_failures++;
+ if (!server)
return;
- }
- sock->type = ctx->conn_type;
- switch(sock->type) {
- case SILC_SOCKET_TYPE_CLIENT:
- {
- SilcClientEntry client;
-
- SILC_LOG_DEBUG(("Remote host is client"));
- SILC_LOG_INFO(("Connection from %s (%s) is client", sock->hostname,
- sock->ip));
-
- /* Add the client to the client ID cache. The nickname and Client ID
- and other information is created after we have received NEW_CLIENT
- packet from client. */
- client = silc_idlist_add_client(server->local_list,
- NULL, 0, NULL, NULL, NULL, NULL, sock);
- if (!client) {
- SILC_LOG_ERROR(("Could not add new client to cache"));
- silc_free(sock->user_data);
- break;
- }
+ silc_server_backup_free(server);
+ silc_server_config_unref(&server->config_ref);
+ if (server->rng)
+ silc_rng_free(server->rng);
+ 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) {
+ if (server->id_entry->data.sconn)
+ silc_schedule_task_del_by_context(server->schedule,
+ server->id_entry->data.sconn->sock);
+ silc_idlist_del_server(server->local_list, server->id_entry);
+ }
- /* Statistics */
- server->stat.my_clients++;
- server->stat.clients++;
- if (server->server_type == SILC_ROUTER)
- server->stat.cell_clients++;
+ /* Delete all channels */
+ if (silc_idcache_get_all(server->local_list->channels, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list)))
+ silc_idlist_del_channel(server->local_list, cache->context);
+ }
+ if (silc_idcache_get_all(server->global_list->channels, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list)))
+ silc_idlist_del_channel(server->global_list, cache->context);
+ }
- id_entry = (void *)client;
- break;
+ /* Delete all clients */
+ if (silc_idcache_get_all(server->local_list->clients, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list))) {
+ silc_schedule_task_del_by_context(server->schedule, cache->context);
+ silc_idlist_del_client(server->local_list, cache->context);
}
- case SILC_SOCKET_TYPE_SERVER:
- case SILC_SOCKET_TYPE_ROUTER:
- {
- SilcServerEntry new_server;
+ }
+ if (silc_idcache_get_all(server->global_list->clients, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list))) {
+ silc_schedule_task_del_by_context(server->schedule, cache->context);
+ silc_idlist_del_client(server->global_list, cache->context);
+ }
+ }
- SILC_LOG_DEBUG(("Remote host is %s",
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- "server" : "router"));
- SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname,
- sock->ip, sock->type == SILC_SOCKET_TYPE_SERVER ?
- "server" : "router"));
+ /* Delete all servers */
+ if (silc_idcache_get_all(server->local_list->servers, &list)) {
+ silc_list_start(list);
+ while ((cache = silc_list_get(list))) {
+ idata = (SilcIDListData)cache->context;
+ if (idata->sconn)
+ silc_schedule_task_del_by_context(server->schedule,
+ idata->sconn->sock);
+ silc_idlist_del_server(server->local_list, cache->context);
+ }
+ }
+ if (silc_idcache_get_all(server->global_list->servers, &list)) {
+ while ((cache = silc_list_get(list))) {
+ idata = (SilcIDListData)cache->context;
+ if (idata->sconn)
+ silc_schedule_task_del_by_context(server->schedule,
+ idata->sconn->sock);
+ silc_idlist_del_server(server->global_list, cache->context);
+ }
+ }
- /* Add the server into server cache. The server name and Server ID
- is updated after we have received NEW_SERVER packet from the
- server. We mark ourselves as router for this server if we really
- are router. */
- new_server =
- silc_idlist_add_server(server->local_list, NULL,
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- SILC_SERVER : SILC_ROUTER, NULL,
- sock->type == SILC_SOCKET_TYPE_SERVER ?
- server->id_entry : NULL, sock);
- if (!new_server) {
- SILC_LOG_ERROR(("Could not add new server to cache"));
- silc_free(sock->user_data);
- break;
- }
+ silc_schedule_task_del_by_context(server->schedule, server);
+ silc_schedule_uninit(server->schedule);
+ server->schedule = NULL;
+
+ 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_table_free(server->watcher_list_pk);
+ silc_hash_free(server->md5hash);
+ silc_hash_free(server->sha1hash);
+
+ silc_dlist_uninit(server->listeners);
+ silc_dlist_uninit(server->conns);
+ silc_dlist_uninit(server->expired_clients);
+ silc_skr_free(server->repository);
+ silc_packet_engine_stop(server->packet_engine);
+
+ silc_free(server->local_list);
+ silc_free(server->global_list);
+ silc_free(server->server_name);
+ silc_free(server);
+
+ silc_hmac_unregister_all();
+ silc_hash_unregister_all();
+ silc_cipher_unregister_all();
+ silc_pkcs_unregister_all();
+}
- /* Statistics */
- if (sock->type == SILC_SOCKET_TYPE_SERVER)
- server->stat.my_servers++;
- else
- server->stat.my_routers++;
- server->stat.servers++;
+/* Creates a new server listener. */
- id_entry = (void *)new_server;
-
- /* There is connection to other server now, if it is router then
- we will have connection to outside world. If we are router but
- normal server connected to us then we will remain standalone,
- if we are standlone. */
- if (server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER) {
- SILC_LOG_DEBUG(("We are not standalone server anymore"));
- server->standalone = FALSE;
- if (!server->id_entry->router) {
- server->id_entry->router = id_entry;
- server->router = id_entry;
- }
- }
- break;
- }
- default:
- break;
+static SilcNetListener
+silc_server_listen(SilcServer server, const char *server_ip, SilcUInt16 port)
+{
+ SilcNetListener listener;
+
+ listener =
+ silc_net_tcp_create_listener(&server_ip, 1, port, TRUE,
+ server->config->require_reverse_lookup,
+ server->schedule,
+ silc_server_accept_new_connection, server);
+ if (!listener) {
+ SILC_SERVER_LOG_ERROR(("Could not create server listener: %s on %hu",
+ server_ip, port));
+ return NULL;
}
- /* Add the common data structure to the ID entry. */
- if (id_entry)
- silc_idlist_add_data(id_entry, (SilcIDListData)sock->user_data);
-
- /* Add to sockets internal pointer for fast referencing */
- silc_free(sock->user_data);
- sock->user_data = id_entry;
-
- /* 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. XXX hardcoded
- timeout!! */
- hb_context = silc_calloc(1, sizeof(*hb_context));
- hb_context->server = server;
- silc_socket_set_heartbeat(sock, 600, hb_context,
- silc_server_perform_heartbeat,
- server->timeout_queue);
-
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_failure_callback);
- silc_protocol_free(protocol);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- if (ctx->dest_id)
- silc_free(ctx->dest_id);
- silc_free(ctx);
- sock->protocol = NULL;
+ return listener;
}
-/* This function is used to read packets from network and send packets to
- network. This is usually a generic task. */
+/* Adds a secondary listener. */
-SILC_TASK_CALLBACK(silc_server_packet_process)
+SilcBool silc_server_init_secondary(SilcServer server)
{
- SilcServer server = (SilcServer)context;
- SilcSocketConnection sock = server->sockets[fd];
- SilcIDListData idata;
- SilcCipher cipher = NULL;
- SilcHmac hmac = NULL;
- int ret;
+ SilcServerConfigServerInfoInterface *interface;
+ SilcNetListener listener;
+
+ for (interface = server->config->server_info->secondary; interface;
+ interface = interface->next) {
+ listener = silc_server_listen(server, interface->server_ip,
+ interface->port);
+ if (!listener)
+ return FALSE;
+ silc_dlist_add(server->listeners, listener);
+ }
- if (!sock)
- return;
+ return TRUE;
+}
- SILC_LOG_DEBUG(("Processing packet"));
+/* Initializes the entire SILC server. This is called always before running
+ the server. This is called only once at the initialization of the program.
+ This binds the server to its listenning port. After this function returns
+ one should call silc_server_run to start the server. This returns TRUE
+ when everything is ok to run the server. Configuration file must be
+ read and parsed before calling this. */
- /* Packet sending */
+SilcBool silc_server_init(SilcServer server)
+{
+ SilcServerID *id;
+ SilcServerEntry id_entry;
+ SilcNetListener listener;
+ SilcUInt16 *port;
+ char **ip;
- if (type == SILC_TASK_WRITE) {
- /* Do not send data to disconnected connection */
- if (SILC_IS_DISCONNECTED(sock))
- return;
+ SILC_LOG_DEBUG(("Initializing server"));
- server->stat.packets_sent++;
+ server->starttime = time(NULL);
- if (sock->outbuf->data - sock->outbuf->head)
- silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+ /* Take config object for us */
+ silc_server_config_ref(&server->config_ref, server->config,
+ server->config);
- ret = silc_server_packet_send_real(server, sock, TRUE);
+#ifdef SILC_DEBUG
+ /* Set debugging on if configured */
+ if (server->config->debug_string) {
+ silc_log_debug(TRUE);
+ silc_log_set_debug_string(server->config->debug_string);
+ }
+#endif /* SILC_DEBUG */
- /* If returned -2 could not write to connection now, will do
- it later. */
- if (ret == -2)
- return;
+ /* Steal public and private key from the config object */
+ 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;
- if (ret == -1)
- 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(fd);
- SILC_UNSET_OUTBUF_PENDING(sock);
-
- silc_buffer_clear(sock->outbuf);
- return;
- }
+ /* Register all configured ciphers, PKCS and hash functions. */
+ if (!silc_server_config_register_ciphers(server))
+ silc_cipher_register_default();
+ if (!silc_server_config_register_pkcs(server))
+ silc_pkcs_register_default();
+ if (!silc_server_config_register_hashfuncs(server))
+ silc_hash_register_default();
+ if (!silc_server_config_register_hmacs(server))
+ silc_hmac_register_default();
- /* Packet receiving */
+ /* Initialize random number generator for the server. */
+ server->rng = silc_rng_alloc();
+ silc_rng_init(server->rng);
+ silc_rng_global_init(server->rng);
- /* Read some data from connection */
- ret = silc_packet_receive(sock);
- if (ret < 0)
- return;
-
- /* EOF */
- if (ret == 0) {
- SILC_LOG_DEBUG(("Read EOF"));
-
- /* If connection is disconnecting already we will finally
- close the connection */
- if (SILC_IS_DISCONNECTING(sock)) {
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock);
- silc_server_close_connection(server, sock);
- return;
+ /* Initialize hash functions for server to use */
+ silc_hash_alloc("md5", &server->md5hash);
+ silc_hash_alloc("sha1", &server->sha1hash);
+
+ /* Initialize the scheduler */
+ server->schedule = silc_schedule_init(server->config->param.connections_max,
+ server);
+ if (!server->schedule)
+ goto err;
+
+ /* First, register log files configuration for error output */
+ silc_server_config_setlogfiles(server);
+
+ /* Initialize ID caches */
+ server->local_list->clients =
+ silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+ server);
+ server->local_list->servers =
+ silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+ server);
+ server->local_list->channels =
+ silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+ NULL);
+
+ /* These are allocated for normal server as well as these hold some
+ global information that the server has fetched from its router. For
+ router these are used as they are supposed to be used on router. */
+ server->global_list->clients =
+ silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+ server);
+ server->global_list->servers =
+ silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+ server);
+ server->global_list->channels =
+ silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+ NULL);
+
+ /* Init watcher lists */
+ 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);
+ if (!server->watcher_list)
+ goto err;
+ server->watcher_list_pk =
+ silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+ silc_hash_public_key_compare, NULL,
+ NULL, NULL, TRUE);
+ if (!server->watcher_list_pk)
+ goto err;
+
+ /* Create TCP listener */
+ listener = silc_server_listen(
+ server,
+ server->config->server_info->primary == NULL ? NULL :
+ server->config->server_info->primary->server_ip,
+ server->config->server_info->primary == NULL ? 0 :
+ server->config->server_info->primary->port);
+ if (!listener)
+ goto err;
+ silc_dlist_add(server->listeners, listener);
+
+ /* Create a Server ID for the server. */
+ port = silc_net_listener_get_port(listener, NULL);
+ ip = silc_net_listener_get_ip(listener, NULL);
+ silc_id_create_server_id(server->config->server_info->primary->public_ip ?
+ server->config->server_info->primary->public_ip :
+ ip[0], port[0], server->rng, &id);
+ if (!id)
+ goto err;
+
+ silc_free(port);
+ silc_free(ip[0]);
+ silc_free(ip);
+
+ server->id = id;
+ server->server_name = server->config->server_info->server_name;
+ server->config->server_info->server_name = NULL;
+ silc_id_id2str(server->id, SILC_ID_SERVER, server->id_string,
+ sizeof(server->id_string), &server->id_string_len);
+
+ /* Add ourselves to the server list. We don't have a router yet
+ beacuse we haven't established a route yet. It will be done later.
+ For now, NULL is sent as router. This allocates new entry to
+ the ID list. */
+ id_entry =
+ silc_idlist_add_server(server->local_list, strdup(server->server_name),
+ server->server_type,
+ silc_id_dup(server->id, SILC_ID_SERVER),
+ NULL, NULL);
+ if (!id_entry) {
+ SILC_LOG_ERROR(("Could not add local server to cache"));
+ goto err;
+ }
+ id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ id_entry->data.conn_type = (server->server_type == SILC_SERVER ?
+ SILC_CONN_SERVER : SILC_CONN_ROUTER);
+ server->id_entry = id_entry;
+
+ /* Create secondary TCP listeners */
+ if (silc_server_init_secondary(server) == FALSE)
+ goto err;
+
+ server->listenning = TRUE;
+
+ /* Create connections to configured routers. */
+ silc_server_create_connections(server);
+
+ /* If server connections has been configured then we must be router as
+ normal server cannot have server connections, only router connections. */
+ if (server->config->servers) {
+ SilcServerConfigServer *ptr = server->config->servers;
+
+ server->server_type = SILC_ROUTER;
+ while (ptr) {
+ if (ptr->backup_router) {
+ server->server_type = SILC_BACKUP_ROUTER;
+ server->backup_router = TRUE;
+ server->id_entry->server_type = SILC_BACKUP_ROUTER;
+ break;
+ }
+ ptr = ptr->next;
}
-
- SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
- SILC_SET_DISCONNECTING(sock);
-
- /* If the closed connection was our primary router connection the
- start re-connecting phase. */
- if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER &&
- sock == server->router->connection)
- silc_task_register(server->timeout_queue, 0,
- silc_server_connect_to_router,
- context, 1, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
-
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock);
- silc_server_close_connection(server, sock);
- return;
}
- /* If connection is disconnecting or disconnected we will ignore
- what we read. */
- if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
- SILC_LOG_DEBUG(("Ignoring read data from disonnected connection"));
- return;
+ if (server->server_type != SILC_ROUTER) {
+ server->stat.servers = 1;
+ server->stat.cell_servers = 1;
+ } else {
+ server->stat.routers = 1;
}
- server->stat.packets_received++;
+ /* If we are normal server we'll retrieve network statisticial information
+ once in a while from the router. */
+ if (server->server_type != SILC_ROUTER)
+ silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
+ server, 10, 0);
- /* Get keys and stuff from ID entry */
- idata = (SilcIDListData)sock->user_data;
- if (idata) {
- idata->last_receive = time(NULL);
- cipher = idata->receive_key;
- hmac = idata->hmac;
- }
-
- /* Process the packet. This will call the parser that will then
- decrypt and parse the packet. */
- silc_packet_receive_process(sock, cipher, hmac, silc_server_packet_parse,
- server);
+ /* Start packet engine */
+ server->packet_engine =
+ silc_packet_engine_start(server->rng, server->server_type == SILC_ROUTER,
+ &silc_server_stream_cbs, server);
+ if (!server->packet_engine)
+ goto err;
+
+ /* Register client entry expiration timeout */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_purge_expired_clients, server,
+ 120, 0);
+
+ /* Initialize HTTP server */
+ silc_server_http_init(server);
+
+ SILC_LOG_DEBUG(("Server initialized"));
+
+ /* We are done here, return succesfully */
+ return TRUE;
+
+ err:
+ silc_server_config_unref(&server->config_ref);
+ return FALSE;
}
-/* Callback function that the silc_packet_decrypt will call to make the
- decision whether the packet is normal or special packet. We will
- return TRUE if it is normal and FALSE if it is special */
+/* Task callback to close a socket connection after rehash */
-static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
- SilcBuffer buffer,
- SilcPacketContext *packet,
- void *context)
+SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
{
- SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
- SilcServer server = (SilcServer)parse_ctx->context;
+ SilcServer server = app_context;
+ SilcPacketStream sock = context;
+ SilcIDListData idata = silc_packet_get_context(sock);
+ const char *hostname;
+ SilcUInt16 port;
+
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, NULL, &port);
+
+ SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
+ hostname, port, SILC_CONNTYPE_STRING(idata->conn_type)));
+ 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");
+ silc_server_free_sock_user_data(server, sock, NULL);
+}
- /* Packet is normal packet, if:
+/* 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.
+ Keep in mind that we no longer have the root privileges at this point. */
+
+SilcBool silc_server_rehash(SilcServer server)
+{
+ SilcServerConfig newconfig;
- 1) packet is private message packet and does not have private key set
- 2) is other packet than channel message packet
- 3) is channel message packet and remote is router and we are router
+ SILC_LOG_INFO(("Rehashing server"));
- all other packets are special packets
- */
+ /* Reset the logging system */
+ silc_log_quick(TRUE);
+ silc_log_flush_all();
- if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
- (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+ /* Start the main rehash phase (read again the config file) */
+ newconfig = silc_server_config_alloc(server->config_file, server);
+ if (!newconfig) {
+ SILC_LOG_ERROR(("Rehash FAILED."));
return FALSE;
+ }
- if (packet_type != SILC_PACKET_CHANNEL_MESSAGE ||
- (packet_type == SILC_PACKET_CHANNEL_MESSAGE &&
- parse_ctx->sock->type == SILC_SOCKET_TYPE_ROUTER &&
- server->server_type == SILC_ROUTER))
- return TRUE;
+ /* Fix the server_name field */
+ if (strcmp(server->server_name, newconfig->server_info->server_name)) {
+ silc_free(server->server_name);
+
+ /* Check server name */
+ server->server_name =
+ silc_identifier_check(newconfig->server_info->server_name,
+ strlen(newconfig->server_info->server_name),
+ SILC_STRING_LOCALE, 256, NULL);
+ if (!server->server_name) {
+ SILC_LOG_ERROR(("Malformed server name string '%s'",
+ server->config->server_info->server_name));
+ return FALSE;
+ }
- return FALSE;
-}
+ /* 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_update_by_context(server->local_list->servers,
+ server->id_entry, NULL,
+ strdup(server->id_entry->server_name),
+ TRUE);
+ }
-/* Parses whole packet, received earlier. */
+ /* Set logging */
+ silc_server_config_setlogfiles(server);
+
+ /* Change new key pair if necessary */
+ if (newconfig->server_info->public_key &&
+ !silc_pkcs_public_key_compare(server->public_key,
+ newconfig->server_info->public_key)) {
+ silc_pkcs_public_key_free(server->public_key);
+ silc_pkcs_private_key_free(server->private_key);
+ 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;
+ }
-SILC_TASK_CALLBACK(silc_server_packet_parse_real)
-{
- SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
- SilcServer server = (SilcServer)parse_ctx->context;
- SilcSocketConnection sock = parse_ctx->sock;
- SilcPacketContext *packet = parse_ctx->packet;
- int ret;
+ /* Check for unconfigured server and router connections and close
+ connections that were unconfigured. */
- SILC_LOG_DEBUG(("Start"));
+ if (server->config->routers) {
+ SilcServerConfigRouter *ptr;
+ SilcServerConfigRouter *newptr;
+ SilcBool found;
- /* Decrypt the received packet */
- ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac,
- packet->buffer, packet,
- silc_server_packet_decrypt_check, parse_ctx);
- if (ret < 0)
- goto out;
+ for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+ found = FALSE;
- if (ret == 0) {
- /* Parse the packet. Packet type is returned. */
- ret = silc_packet_parse(packet);
- } else {
- /* Parse the packet header in special way as this is "special"
- packet type. */
- ret = silc_packet_parse_special(packet);
+ /* 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 */
+ SilcPacketStream sock;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+ ptr->host, ptr->port);
+ if (sock)
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_rehash_close_connection,
+ sock, 0, 1);
+ }
+ }
}
- if (ret == SILC_PACKET_NONE)
- goto out;
+ if (server->config->servers) {
+ SilcServerConfigServer *ptr;
+ SilcServerConfigServer *newptr;
+ SilcBool found;
- /* 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) {
- void *id = silc_id_str2id(packet->src_id, packet->src_id_len,
- packet->src_id_type);
- if (SILC_ID_CLIENT_COMPARE(client->id, id)) {
- silc_free(id);
- goto out;
+ 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 */
+ SilcPacketStream sock;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_SERVER,
+ ptr->host, 0);
+ if (sock)
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_rehash_close_connection,
+ sock, 0, 1);
}
- 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->dst_id_type == SILC_ID_SERVER &&
- sock->type != SILC_SOCKET_TYPE_CLIENT &&
- SILC_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
-
- /* Route the packet to fastest route for the destination ID */
- void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
- packet->dst_id_type);
- if (!id)
- goto out;
- silc_server_packet_route(server,
- silc_server_route_get(server, id,
- packet->dst_id_type),
- packet);
- silc_free(id);
- goto out;
+ if (server->config->clients) {
+ SilcServerConfigClient *ptr;
+ SilcServerConfigClient *newptr;
+ SilcBool 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 */
+ SilcPacketStream sock;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_CLIENT,
+ ptr->host, 0);
+ if (sock)
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_rehash_close_connection,
+ sock, 0, 1);
+ }
}
-
- /* 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) {
- silc_server_packet_broadcast(server, server->router->connection, packet);
+ }
+
+ /* Create connections after rehash */
+ silc_server_create_connections(server);
+
+ /* Check whether our router status has changed */
+ if (newconfig->servers) {
+ SilcServerConfigServer *ptr = newconfig->servers;
+
+ server->server_type = SILC_ROUTER;
+ while (ptr) {
+ if (ptr->backup_router) {
+ server->server_type = SILC_BACKUP_ROUTER;
+ server->backup_router = TRUE;
+ server->id_entry->server_type = SILC_BACKUP_ROUTER;
+ break;
+ }
+ ptr = ptr->next;
}
}
- /* Parse the incoming packet type */
- silc_server_packet_parse_type(server, sock, packet);
+ /* 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);
+
+#ifdef SILC_DEBUG
+ /* Set debugging on if configured */
+ if (server->config->debug_string) {
+ silc_log_debug(TRUE);
+ silc_log_set_debug_string(server->config->debug_string);
+ }
+#endif /* SILC_DEBUG */
+
+ SILC_LOG_DEBUG(("Server rehashed"));
- out:
- /* silc_buffer_clear(sock->inbuf); */
- silc_packet_context_free(packet);
- silc_free(parse_ctx);
+ return TRUE;
}
-/* Parser callback called by silc_packet_receive_process. This merely
- registers timeout that will handle the actual parsing when appropriate. */
+/* The heart of the server. This runs the scheduler thus runs the server.
+ When this returns the server has been stopped and the program will
+ be terminated. */
-void silc_server_packet_parse(SilcPacketParserContext *parser_context)
+void silc_server_run(SilcServer server)
{
- SilcServer server = (SilcServer)parser_context->context;
- SilcSocketConnection sock = parser_context->sock;
+ SILC_LOG_INFO(("SILC Server started"));
- switch (sock->type) {
- case SILC_SOCKET_TYPE_CLIENT:
- case SILC_SOCKET_TYPE_UNKNOWN:
- /* Parse the packet with timeout */
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_packet_parse_real,
- (void *)parser_context, 0, 100000,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- break;
- case SILC_SOCKET_TYPE_SERVER:
- case SILC_SOCKET_TYPE_ROUTER:
- /* Packets from servers are parsed as soon as possible */
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_packet_parse_real,
- (void *)parser_context, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- break;
- default:
- return;
- }
+ /* Start the scheduler, the heart of the SILC server. When this returns
+ the program will be terminated. */
+ silc_schedule(server->schedule);
}
-/* Parses the packet type and calls what ever routines the packet type
- requires. This is done for all incoming packets. */
+/* Stops the SILC server. This function is used to shutdown the server.
+ This is usually called after the scheduler has returned. After stopping
+ the server one should call silc_server_free. */
-void silc_server_packet_parse_type(SilcServer server,
- SilcSocketConnection sock,
- SilcPacketContext *packet)
+void silc_server_stop(SilcServer server)
{
- SilcPacketType type = packet->type;
+ SilcDList list;
+ SilcPacketStream ps;
+ SilcNetListener listener;
- SILC_LOG_DEBUG(("Parsing packet type %d", type));
+ SILC_LOG_INFO(("SILC Server shutting down"));
- /* Parse the packet type */
- switch(type) {
- case SILC_PACKET_DISCONNECT:
- SILC_LOG_DEBUG(("Disconnect packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- break;
+ server->server_shutdown = TRUE;
- case SILC_PACKET_SUCCESS:
- /*
- * Success received for something. For now we can have only
- * 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) {
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
- }
- break;
+ /* Close all connections */
+ if (server->packet_engine) {
+ list = silc_packet_engine_get_streams(server->packet_engine);
- case SILC_PACKET_FAILURE:
- /*
- * Failure received for something. For now we can have only
- * 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;
-
- /* We will wait 5 seconds to process this failure packet */
- silc_task_register(server->timeout_queue, sock->sock,
- silc_server_failure_callback, (void *)f, 5, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
- }
- break;
+ silc_dlist_start(list);
+ while ((ps = silc_dlist_get(list))) {
+ SilcIDListData idata = silc_packet_get_context(ps);
- case SILC_PACKET_REJECT:
- SILC_LOG_DEBUG(("Reject packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- return;
- break;
+ if (!silc_packet_stream_is_valid(ps))
+ continue;
- case SILC_PACKET_NOTIFY:
- /*
- * 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
- silc_server_notify(server, sock, packet);
- break;
+ if (idata)
+ idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
- /*
- * Channel packets
- */
- case SILC_PACKET_CHANNEL_MESSAGE:
- /*
- * Received channel message. Channel messages are special packets
- * (although probably most common ones) thus they are handled
- * specially.
- */
- SILC_LOG_DEBUG(("Channel Message packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- silc_server_channel_message(server, sock, packet);
- break;
+ silc_server_disconnect_remote(server, ps, SILC_STATUS_OK,
+ "Server is shutting down");
+ silc_server_free_sock_user_data(server, ps,
+ "Server is shutting down");
+ }
+ silc_packet_engine_free_streams_list(list);
+ }
- case SILC_PACKET_CHANNEL_KEY:
- /*
- * Received key for channel. As channels are created by the router
- * the keys are as well. We will distribute the key to all of our
- * 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);
- break;
+ /* We are not connected to network anymore */
+ server->standalone = TRUE;
- /*
- * Command packets
- */
- case SILC_PACKET_COMMAND:
- /*
- * 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);
- break;
+ silc_dlist_start(server->listeners);
+ while ((listener = silc_dlist_get(server->listeners)))
+ silc_net_close_listener(listener);
- case SILC_PACKET_COMMAND_REPLY:
- /*
- * Received command reply packet. Received command reply to command. It
- * 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);
- break;
+ silc_server_http_uninit(server);
- /*
- * Private Message packets
- */
- case SILC_PACKET_PRIVATE_MESSAGE:
- /*
- * 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;
- silc_server_private_message(server, sock, packet);
- break;
+ /* 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);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_to_router);
- case SILC_PACKET_PRIVATE_MESSAGE_KEY:
- /*
- * Private message key packet.
- */
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- silc_server_private_message_key(server, sock, packet);
- break;
+ silc_schedule_stop(server->schedule);
- /*
- * Key Exchange protocol packets
- */
- case SILC_PACKET_KEY_EXCHANGE:
- SILC_LOG_DEBUG(("KE packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
+ SILC_LOG_DEBUG(("Server stopped"));
+}
- if (sock->protocol && sock->protocol->protocol &&
- sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+/* Purge expired client entries from the server */
- SilcServerKEInternalContext *proto_ctx =
- (SilcServerKEInternalContext *)sock->protocol->context;
+SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
+{
+ SilcServer server = context;
+ SilcClientEntry client;
+ SilcIDList id_list;
+ SilcUInt64 curtime = silc_time();
- proto_ctx->packet = silc_packet_context_dup(packet);
+ SILC_LOG_DEBUG(("Expire timeout"));
- /* Let the protocol handle the packet */
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 100000);
- } else {
- SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
- "protocol active, packet dropped."));
+ silc_dlist_start(server->expired_clients);
+ while ((client = silc_dlist_get(server->expired_clients))) {
+ /* For unregistered clients the created timestamp is actually
+ unregistered timestamp. Make sure client remains in history
+ at least 500 seconds. */
+ if (client->data.created && curtime - client->data.created < 500)
+ continue;
- /* XXX Trigger KE protocol?? Rekey actually, maybe. */
- }
- break;
+ id_list = (client->data.status & SILC_IDLIST_STATUS_LOCAL ?
+ server->local_list : server->global_list);
- case SILC_PACKET_KEY_EXCHANGE_1:
- SILC_LOG_DEBUG(("KE 1 packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
+ silc_idlist_del_data(client);
+ silc_idlist_del_client(id_list, client);
+ silc_dlist_del(server->expired_clients, client);
+ }
- if (sock->protocol && sock->protocol->protocol &&
- sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_purge_expired_clients, server,
+ 120, 0);
+}
- SilcServerKEInternalContext *proto_ctx =
- (SilcServerKEInternalContext *)sock->protocol->context;
- if (proto_ctx->packet)
- silc_packet_context_free(proto_ctx->packet);
+/******************************* Connecting *********************************/
- proto_ctx->packet = silc_packet_context_dup(packet);
- proto_ctx->dest_id_type = packet->src_id_type;
- proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
- packet->src_id_type);
- if (!proto_ctx->dest_id)
- break;
+/* Free connection context */
- /* Let the protocol handle the packet */
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock,
- 0, 100000);
- } else {
- SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
- "protocol active, packet dropped."));
- }
- break;
+void silc_server_connection_free(SilcServerConnection sconn)
+{
+ if (!sconn)
+ return;
+ SILC_LOG_DEBUG(("Free connection %p", sconn));
+ silc_dlist_del(sconn->server->conns, sconn);
+ silc_server_config_unref(&sconn->conn);
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
+}
- case SILC_PACKET_KEY_EXCHANGE_2:
- SILC_LOG_DEBUG(("KE 2 packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
+/* Creates connection to a remote router. */
- if (sock->protocol && sock->protocol->protocol &&
- sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+void silc_server_create_connection(SilcServer server,
+ SilcBool reconnect,
+ SilcBool dynamic,
+ const char *remote_host, SilcUInt32 port,
+ SilcServerConnectCallback callback,
+ void *context)
+{
+ SilcServerConnection sconn;
- SilcServerKEInternalContext *proto_ctx =
- (SilcServerKEInternalContext *)sock->protocol->context;
+ /* Allocate connection object for hold connection specific stuff. */
+ sconn = silc_calloc(1, sizeof(*sconn));
+ if (!sconn)
+ return;
+ sconn->remote_host = strdup(remote_host);
+ sconn->remote_port = port;
+ sconn->no_reconnect = reconnect == FALSE;
+ sconn->callback = callback;
+ sconn->callback_context = context;
+ sconn->no_conf = dynamic;
+ sconn->server = server;
- if (proto_ctx->packet)
- silc_packet_context_free(proto_ctx->packet);
+ SILC_LOG_DEBUG(("Created connection %p", sconn));
- proto_ctx->packet = silc_packet_context_dup(packet);
- proto_ctx->dest_id_type = packet->src_id_type;
- proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
- packet->src_id_type);
- if (!proto_ctx->dest_id)
- break;
+ silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
+ sconn, 0, 0);
+}
- /* Let the protocol handle the packet */
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock,
- 0, 100000);
- } else {
- SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
- "protocol active, packet dropped."));
+/* Connection authentication completion callback */
+
+static void
+silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
+ void *context)
+{
+ SilcServerConnection sconn = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sconn->sock);
+ SilcServer server = entry->server;
+ SilcServerConfigServer *conn;
+ SilcServerConfigConnParams *param;
+ SilcIDListData idata;
+ SilcServerEntry id_entry = NULL;
+ unsigned char id[32];
+ SilcUInt32 id_len;
+ SilcID remote_id;
+ const char *ip;
+
+ SILC_LOG_DEBUG(("Connection %p authentication completed, entry %p",
+ sconn, entry));
+
+ entry->op = NULL;
+
+ if (success == FALSE) {
+ /* Authentication failed */
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
+ return;
+ }
+
+ /* XXX For now remote is router always */
+ entry->data.conn_type = SILC_CONN_ROUTER;
+
+ SILC_LOG_INFO(("Connected to %s %s",
+ SILC_CONNTYPE_STRING(entry->data.conn_type),
+ sconn->remote_host));
+
+ /* Create the actual entry for remote entity */
+ switch (entry->data.conn_type) {
+ case SILC_CONN_SERVER:
+ SILC_LOG_DEBUG(("Remote is SILC server"));
+
+ /* Add new server. The server must register itself to us before it
+ becomes registered to SILC network. */
+ id_entry = silc_idlist_add_server(server->local_list,
+ strdup(sconn->remote_host),
+ SILC_SERVER, NULL, NULL, sconn->sock);
+ if (!id_entry) {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
}
+
+ /* Statistics */
+ server->stat.my_servers++;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.servers++;
+ SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+
+ silc_idlist_add_data(id_entry, (SilcIDListData)entry);
break;
- case SILC_PACKET_CONNECTION_AUTH_REQUEST:
- /*
- * Connection authentication request packet. When we receive this packet
- * we will send to the other end information about our mandatory
- * 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_CONN_ROUTER:
+ SILC_LOG_DEBUG(("Remote is SILC router"));
+
+ /* Register to network */
+ silc_id_id2str(server->id, SILC_ID_SERVER, id, sizeof(id), &id_len);
+ if (!silc_packet_send_va(sconn->sock, SILC_PACKET_NEW_SERVER, 0,
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_DATA(id, id_len),
+ SILC_STR_UI_SHORT(strlen(server->server_name)),
+ SILC_STR_DATA(server->server_name,
+ strlen(server->server_name)),
+ SILC_STR_END)) {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+
+ /* Get remote ID */
+ silc_packet_get_ids(sconn->sock, NULL, NULL, NULL, &remote_id);
+
+ /* Check that we do not have this ID already */
+ id_entry = silc_idlist_find_server_by_id(server->local_list,
+ &remote_id.u.server_id,
+ TRUE, NULL);
+ if (id_entry) {
+ silc_idcache_del_by_context(server->local_list->servers, id_entry, NULL);
+ } else {
+ id_entry = silc_idlist_find_server_by_id(server->global_list,
+ &remote_id.u.server_id,
+ TRUE, NULL);
+ if (id_entry)
+ silc_idcache_del_by_context(server->global_list->servers, id_entry,
+ NULL);
+ }
+
+ SILC_LOG_DEBUG(("New server id(%s)",
+ silc_id_render(&remote_id.u.server_id, SILC_ID_SERVER)));
+
+ /* 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(sconn->remote_host),
+ SILC_ROUTER,
+ silc_id_dup(&remote_id.u.server_id,
+ SILC_ID_SERVER),
+ NULL, sconn->sock);
+ if (!id_entry) {
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+
+ /* Registered */
+ silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+ idata = (SilcIDListData)id_entry;
+ idata->status |= (SILC_IDLIST_STATUS_REGISTERED |
+ SILC_IDLIST_STATUS_LOCAL);
+ idata->sconn = sconn;
+
+ /* Statistics */
+ server->stat.my_routers++;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.routers++;
+ SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+
+ 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->router->server_type = SILC_ROUTER;
+ server->standalone = FALSE;
+ server->backup_primary = FALSE;
+
+ /* Announce data if we are not backup router (unless not as primary
+ currently). Backup router announces later at the end of
+ resuming protocol. */
+ if (server->backup_router && server->server_type == SILC_ROUTER) {
+ SILC_LOG_DEBUG(("Announce data after resume protocol"));
+ } else {
+ /* 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,
+ server->backup_router ? TRUE : FALSE,
+ 0, SILC_PRIMARY_ROUTE(server));
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
+ silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+ }
+
+ /* If we are backup router then this primary router is whom we are
+ backing up. */
+ if (server->server_type == SILC_BACKUP_ROUTER) {
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sconn->
+ sock),
+ NULL, NULL, &ip, NULL);
+ silc_server_backup_add(server, server->id_entry, ip,
+ sconn->remote_port, TRUE);
+ }
+ }
+#if 0
+ else {
+ /* We already have primary router. Disconnect this connection */
+ SILC_LOG_DEBUG(("We already have primary router, disconnect"));
+ silc_idlist_del_server(server->global_list, id_entry);
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+#endif /* 0 */
+ } else {
+ /* Add this server to be our backup router */
+ id_entry->server_type = SILC_BACKUP_ROUTER;
+ silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
+ sconn->backup_replace_port, FALSE);
+ }
+
break;
- /*
- * Connection Authentication protocol packets
- */
- 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;
+ default:
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Connection established, sock %p", sconn->sock));
+
+ conn = sconn->conn.ref_ptr;
+ param = &server->config->param;
+ if (conn && conn->param)
+ param = conn->param;
- if (sock->protocol && sock->protocol->protocol->type
- == SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
+ /* Register rekey timeout */
+ sconn->rekey_timeout = param->key_exchange_rekey;
+ silc_schedule_task_add_timeout(server->schedule, silc_server_do_rekey,
+ sconn->sock, sconn->rekey_timeout, 0);
- SilcServerConnAuthInternalContext *proto_ctx =
- (SilcServerConnAuthInternalContext *)sock->protocol->context;
+ /* Set the entry as packet stream context */
+ silc_packet_set_context(sconn->sock, id_entry);
- proto_ctx->packet = silc_packet_context_dup(packet);
+ /* Call the completion callback to indicate that we've connected to
+ the router */
+ if (sconn && sconn->callback)
+ (*sconn->callback)(server, id_entry, sconn->callback_context);
+
+ if (sconn == server->router_conn)
+ server->router_conn = NULL;
+
+ silc_free(entry);
+}
- /* Let the protocol handle the packet */
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
+/* SKE completion callback */
+
+static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
+ SilcSKESecurityProperties prop,
+ SilcSKEKeyMaterial keymat,
+ SilcSKERekeyMaterial rekey,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
+ SilcServerConnection sconn = entry->data.sconn;
+ SilcServer server = entry->server;
+ SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+ SilcAuthMethod auth_meth = SILC_AUTH_NONE;
+ void *auth_data = NULL;
+ SilcUInt32 auth_data_len = 0;
+ SilcConnAuth connauth;
+ SilcCipher send_key, receive_key;
+ SilcHmac hmac_send, hmac_receive;
+ SilcHash hash;
+
+ SILC_LOG_DEBUG(("Connection %p, SKE completed, entry %p", sconn, entry));
+
+ entry->op = NULL;
+
+ if (status != SILC_SKE_STATUS_OK) {
+ /* SKE failed */
+ SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+ silc_ske_map_status(status), entry->hostname, entry->ip));
+ silc_ske_free(ske);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Setting keys into use"));
+
+ /* Set the keys into use. The data will be encrypted after this. */
+ if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
+ &hmac_send, &hmac_receive, &hash)) {
+ silc_ske_free(ske);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ /* Error setting keys */
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ return;
+ }
+ silc_packet_set_keys(sconn->sock, send_key, receive_key, hmac_send,
+ hmac_receive, FALSE);
+
+ SILC_LOG_DEBUG(("Starting connection authentication"));
+
+ connauth = silc_connauth_alloc(server->schedule, ske,
+ server->config->conn_auth_timeout);
+ if (!connauth) {
+ silc_ske_free(ske);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ /** Error allocating auth protocol */
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+
+ /* Get authentication method */
+ if (conn) {
+ if (conn->passphrase) {
+ if (conn->publickeys && !server->config->prefer_passphrase_auth) {
+ auth_meth = SILC_AUTH_PUBLIC_KEY;
+ auth_data = server->private_key;
+ } else {
+ auth_meth = SILC_AUTH_PASSWORD;
+ auth_data = conn->passphrase;
+ auth_data_len = conn->passphrase_len;
+ }
} else {
- SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
- "protocol active, packet dropped."));
+ auth_meth = SILC_AUTH_PUBLIC_KEY;
+ auth_data = server->private_key;
}
+ }
+
+ entry->data.rekey = rekey;
+
+ /* Start connection authentication */
+ entry->op =
+ silc_connauth_initiator(connauth, server->server_type == SILC_SERVER ?
+ SILC_CONN_SERVER : SILC_CONN_ROUTER, auth_meth,
+ auth_data, auth_data_len,
+ silc_server_ke_auth_compl, sconn);
+}
+
+/* Function that is called when the network connection to a router has
+ been established. This will continue with the key exchange protocol
+ with the remote router. */
+
+void silc_server_start_key_exchange(SilcServerConnection sconn)
+{
+ SilcServer server = sconn->server;
+ SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+ SilcUnknownEntry entry;
+ SilcSKEParamsStruct params;
+ SilcSKE ske;
+
+ /* Cancel any possible retry timeouts */
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+
+ /* Create packet stream */
+ sconn->sock = silc_packet_stream_create(server->packet_engine,
+ server->schedule, sconn->stream);
+ if (!sconn->sock) {
+ SILC_LOG_ERROR(("Cannot connect: cannot create packet stream"));
+ silc_stream_destroy(sconn->stream);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ server->stat.conn_num++;
+
+ /* Set source ID to packet stream */
+ if (!silc_packet_set_ids(sconn->sock, SILC_ID_SERVER, server->id,
+ 0, NULL)) {
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+
+ /* Create entry for remote entity */
+ entry = silc_calloc(1, sizeof(*entry));
+ if (!entry) {
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ entry->server = server;
+ entry->data.sconn = sconn;
+ silc_packet_set_context(sconn->sock, entry);
+
+ SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
+ /* Set Key Exchange flags from configuration, but fall back to global
+ settings too. */
+ memset(¶ms, 0, sizeof(params));
+ SILC_GET_SKE_FLAGS(conn, params.flags);
+ if (server->config->param.key_exchange_pfs)
+ params.flags |= SILC_SKE_SP_FLAG_PFS;
+
+ /* Start SILC Key Exchange protocol */
+ SILC_LOG_DEBUG(("Starting key exchange protocol, connection %p", sconn));
+ ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
+ server->public_key, server->private_key, sconn);
+ if (!ske) {
+ silc_free(entry);
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ silc_ske_set_callbacks(ske, silc_server_verify_key,
+ silc_server_ke_completed, sconn->sock);
+
+ /* Start key exchange protocol */
+ params.version = silc_version_string;
+ params.timeout_secs = server->config->key_exchange_timeout;
+ entry->op = silc_ske_initiator(ske, sconn->sock, ¶ms, NULL);
+}
+
+/* Timeout callback that will be called to retry connecting to remote
+ router. This is used by both normal and router server. This will wait
+ before retrying the connecting. The timeout is generated by exponential
+ backoff algorithm. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
+{
+ SilcServerConnection sconn = context;
+ SilcServer server = sconn->server;
+ SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+ SilcServerConfigConnParams *param =
+ (conn->param ? conn->param : &server->config->param);
+
+ SILC_LOG_INFO(("Retrying connecting to %s:%d", sconn->remote_host,
+ sconn->remote_port));
+
+ /* Calculate next timeout */
+ if (sconn->retry_count >= 1) {
+ sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
+ if (sconn->retry_timeout > param->reconnect_interval_max)
+ sconn->retry_timeout = param->reconnect_interval_max;
+ } else {
+ sconn->retry_timeout = param->reconnect_interval;
+ }
+ sconn->retry_count++;
+ sconn->retry_timeout = sconn->retry_timeout +
+ (silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER);
+
+ /* If we've reached max retry count, give up. */
+ if ((sconn->retry_count > param->reconnect_count) &&
+ sconn->no_reconnect) {
+ SILC_LOG_ERROR(("Could not connect, giving up"));
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Retrying connecting %d seconds", sconn->retry_timeout));
+
+ /* We will lookup a fresh pointer later */
+ silc_server_config_unref(&sconn->conn);
+
+ /* Wait before retrying */
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+ silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
+ sconn, sconn->retry_timeout, 0);
+}
+
+/* Callback for async connection to remote router */
+
+static void silc_server_connection_established(SilcNetStatus status,
+ SilcStream stream,
+ void *context)
+{
+ SilcServerConnection sconn = context;
+ SilcServer server = sconn->server;
+
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+ sconn->op = NULL;
+
+ switch (status) {
+ case SILC_NET_OK:
+ SILC_LOG_DEBUG(("Connection %p to %s:%d established", sconn,
+ sconn->remote_host, sconn->remote_port));
+
+ /* Continue with key exchange protocol */
+ sconn->stream = stream;
+ silc_server_start_key_exchange(sconn);
break;
- case SILC_PACKET_NEW_ID:
- /*
- * Received New ID packet. This includes some new ID that has been
- * created. It may be for client, server or channel. This is the way
- * 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
- silc_server_new_id(server, sock, packet);
+ case SILC_NET_UNKNOWN_IP:
+ case SILC_NET_UNKNOWN_HOST:
+ SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
+ sconn->remote_host, sconn->remote_port,
+ silc_net_get_error_string(status)));
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(sconn->server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ } else {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ }
break;
- case SILC_PACKET_NEW_CLIENT:
- /*
- * Received new client packet. This includes client information that
- * 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);
+ default:
+ SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
+ sconn->remote_host, sconn->remote_port,
+ silc_net_get_error_string(status)));
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(sconn->server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ } else {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ }
break;
+ }
+}
- case SILC_PACKET_NEW_SERVER:
- /*
- * Received new server packet. This includes Server ID and some other
- * 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)
+/* Generic routine to use connect to a router. */
+
+SILC_TASK_CALLBACK(silc_server_connect_router)
+{
+ SilcServerConnection sconn = context;
+ SilcServer server = sconn->server;
+ SilcServerConfigRouter *rconn;
+
+ silc_schedule_task_del_by_context(server->schedule, sconn);
+
+ /* Don't connect if we are shutting down. */
+ if (server->server_shutdown) {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_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));
+
+ if (!sconn->no_conf) {
+ /* Find connection configuration */
+ rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
+ sconn->remote_port);
+ if (!rconn) {
+ SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
+ (sconn->backup ? "backup router" : "router"),
+ sconn->remote_host, sconn->remote_port));
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+ silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
+ }
+
+ /* Connect to remote host */
+ sconn->op =
+ silc_net_tcp_connect((!server->config->server_info->primary ? NULL :
+ server->config->server_info->primary->server_ip),
+ sconn->remote_host, sconn->remote_port,
+ server->schedule, silc_server_connection_established,
+ sconn);
+ if (!sconn->op) {
+ SILC_LOG_ERROR(("Could not connect to router %s:%d",
+ sconn->remote_host, sconn->remote_port));
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ return;
+ }
+
+ /* Add to connection list */
+ silc_dlist_add(server->conns, sconn);
+}
+
+/* This function connects to our primary router or if we are a router this
+ establishes all our primary routes. This is called at the start of the
+ server to do authentication and key exchange with our router - called
+ from schedule. */
+
+SILC_TASK_CALLBACK(silc_server_connect_to_router)
+{
+ SilcServer server = context;
+ SilcServerConnection sconn;
+ SilcServerConfigRouter *ptr;
+ SilcServerConfigConnParams *param;
+
+ /* Don't connect if we are shutting down. */
+ if (server->server_shutdown)
+ return;
+
+ 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), we are standalone"));
+ server->standalone = 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) {
+
+ SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
+ ptr->backup_router ? "Backup router" : "Router",
+ 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)
+ continue;
+ if (ptr->dynamic_connection)
+ continue;
+
+ /* Check whether we are connecting or connected to this host already */
+ if (silc_server_num_sockets_by_remote(server,
+ silc_net_is_ip(ptr->host) ?
+ ptr->host : NULL,
+ silc_net_is_ip(ptr->host) ?
+ NULL : ptr->host, ptr->port,
+ SILC_CONN_ROUTER)) {
+ SILC_LOG_DEBUG(("We are already connected to %s:%d",
+ ptr->host, ptr->port));
+
+ /* If we don't have primary router and this connection is our
+ primary router we are in desync. Reconnect to the primary. */
+ if (server->standalone && !server->router) {
+ /* XXX */
+ SilcPacketStream sock;
+ SilcServerConfigRouter *primary =
+ silc_server_config_get_primary_router(server);
+ if (primary != ptr)
+ continue;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+ ptr->host, ptr->port);
+ if (!sock)
+ continue;
+ server->backup_noswitch = TRUE;
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_disconnect_remote(server, sock, 0, NULL);
+ server->backup_noswitch = FALSE;
+ SILC_LOG_DEBUG(("Reconnecting to primary router"));
+ } else {
+ continue;
+ }
+ }
+
+ param = (ptr->param ? ptr->param : &server->config->param);
+
+ /* Allocate connection object for hold connection specific stuff. */
+ sconn = silc_calloc(1, sizeof(*sconn));
+ if (!sconn)
+ continue;
+ sconn->server = server;
+ sconn->remote_host = strdup(ptr->host);
+ sconn->remote_port = ptr->port;
+ sconn->backup = ptr->backup_router;
+ if (sconn->backup) {
+ sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
+ sconn->backup_replace_port = ptr->backup_replace_port;
+ }
+ sconn->no_reconnect = param->reconnect_keep_trying == FALSE;
+
+ SILC_LOG_DEBUG(("Created connection %p", sconn));
+
+ if (!server->router_conn && !sconn->backup)
+ server->router_conn = sconn;
+
+ /* Connect */
+ silc_server_connect_router(server->schedule, server, SILC_TASK_EXPIRE,
+ 0, sconn);
+ }
+}
+
+
+/************************ Accepting new connection **************************/
+
+/* 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;
+}
+
+/* Authentication data callback */
+
+static SilcBool
+silc_server_accept_get_auth(SilcConnAuth connauth,
+ SilcConnectionType conn_type,
+ unsigned char **passphrase,
+ SilcUInt32 *passphrase_len,
+ SilcSKR *repository,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
+ SilcServer server = entry->server;
+
+ SILC_LOG_DEBUG(("Remote connection type %d", conn_type));
+
+ /* Remote end is client */
+ if (conn_type == SILC_CONN_CLIENT) {
+ SilcServerConfigClient *cconfig = entry->cconfig.ref_ptr;
+ if (!cconfig)
+ return FALSE;
+
+ *passphrase = cconfig->passphrase;
+ *passphrase_len = cconfig->passphrase_len;
+ if (cconfig->publickeys)
+ *repository = server->repository;
+
+ if (cconfig->publickeys) {
+ if (server->config->prefer_passphrase_auth) {
+ *repository = NULL;
+ } else {
+ *passphrase = NULL;
+ *passphrase_len = 0;
+ }
+ }
+
+ entry->conn_type = conn_type;
+ return TRUE;
+ }
+
+ /* Remote end is server */
+ if (conn_type == SILC_CONN_SERVER) {
+ SilcServerConfigServer *sconfig;
+
+ /* If we are normal server, don't accept the connection */
+ if (server->server_type == SILC_SERVER)
+ return FALSE;
+
+ sconfig = entry->sconfig.ref_ptr;
+ if (!sconfig)
+ return FALSE;
+
+ *passphrase = sconfig->passphrase;
+ *passphrase_len = sconfig->passphrase_len;
+ if (sconfig->publickeys)
+ *repository = server->repository;
+
+ if (sconfig->publickeys) {
+ if (server->config->prefer_passphrase_auth) {
+ *repository = NULL;
+ } else {
+ *passphrase = NULL;
+ *passphrase_len = 0;
+ }
+ }
+
+ entry->conn_type = conn_type;
+ return TRUE;
+ }
+
+ /* Remote end is router */
+ if (conn_type == SILC_CONN_ROUTER) {
+ SilcServerConfigRouter *rconfig = entry->rconfig.ref_ptr;
+ if (!rconfig)
+ return FALSE;
+
+ *passphrase = rconfig->passphrase;
+ *passphrase_len = rconfig->passphrase_len;
+ if (rconfig->publickeys)
+ *repository = server->repository;
+
+ if (rconfig->publickeys) {
+ if (server->config->prefer_passphrase_auth) {
+ *repository = NULL;
+ } else {
+ *passphrase = NULL;
+ *passphrase_len = 0;
+ }
+ }
+
+ entry->conn_type = conn_type;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Authentication completion callback. */
+
+static void
+silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
+ SilcIDListData idata = (SilcIDListData)entry;
+ SilcServer server = entry->server;
+ SilcServerConfigConnParams *param = &server->config->param;
+ SilcServerConnection sconn;
+ void *id_entry;
+ const char *hostname, *ip;
+ SilcUInt16 port;
+
+ entry->op = NULL;
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, &ip, &port);
+
+ if (success == FALSE) {
+ /* Authentication failed */
+ SILC_LOG_INFO(("Authentication failed for %s (%s) [%s]", entry->hostname,
+ entry->ip, SILC_CONNTYPE_STRING(entry->data.conn_type)));
+ server->stat.auth_failures++;
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+
+ SILC_LOG_DEBUG(("Checking whether connection is allowed"));
+
+ switch (entry->conn_type) {
+ case SILC_CONN_CLIENT:
+ {
+ SilcClientEntry client;
+ SilcServerConfigClient *conn = entry->cconfig.ref_ptr;
+
+ /* Verify whether this connection is after all allowed to connect */
+ if (!silc_server_connection_allowed(server, sock, entry->conn_type,
+ &server->config->param,
+ conn->param,
+ silc_connauth_get_ske(connauth))) {
+ server->stat.auth_failures++;
+ 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,
+ entry->ip) &&
+ silc_server_find_socket_by_host(server,
+ SILC_CONN_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_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+
+ /* From here on, wait 20 seconds for the backup router to appear. */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_router_wait,
+ (void *)server, 20, 0);
+ goto out;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Remote host is client"));
+ SILC_LOG_INFO(("Connection %s (%s) is client", entry->hostname,
+ entry->ip));
+
+ /* Add the client to the client ID cache. The nickname and Client ID
+ and other information is created after we have received NEW_CLIENT
+ packet from client. */
+ client = silc_idlist_add_client(server->local_list,
+ NULL, NULL, NULL, NULL, NULL, sock);
+ if (!client) {
+ SILC_LOG_ERROR(("Could not add new client to cache"));
+ server->stat.auth_failures++;
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ goto out;
+ }
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+ entry->data.conn_type = SILC_CONN_CLIENT;
+
+ /* Statistics */
+ server->stat.my_clients++;
+ server->stat.clients++;
+ server->stat.cell_clients++;
+
+ /* Get connection parameters */
+ if (conn->param) {
+ 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;
+ }
+
+ /* Add public key to repository */
+ SILC_LOG_DEBUG(("Add client public key to repository"));
+ if (!silc_server_get_public_key_by_client(server, client, NULL))
+ silc_skr_add_public_key_simple(server->repository,
+ entry->data.public_key,
+ SILC_SKR_USAGE_IDENTIFICATION, client,
+ NULL);
+
+ id_entry = (void *)client;
break;
- silc_server_new_server(server, sock, packet);
- break;
+ }
- case SILC_PACKET_NEW_CHANNEL:
- /*
- * 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
- silc_server_new_channel(server, sock, packet);
+ case SILC_CONN_SERVER:
+ case SILC_CONN_ROUTER:
+ {
+ SilcServerEntry new_server;
+ SilcBool initiator = FALSE;
+ SilcBool backup_local = FALSE;
+ SilcBool backup_router = FALSE;
+ char *backup_replace_ip = NULL;
+ SilcUInt16 backup_replace_port = 0;
+ SilcServerConfigServer *srvconn = entry->sconfig.ref_ptr;
+ SilcServerConfigRouter *rconn = entry->rconfig.ref_ptr;
+
+ /* 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 &&
+ entry->conn_type == SILC_CONN_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_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+ goto out;
+ }
+
+ if (entry->conn_type == SILC_CONN_ROUTER) {
+ /* Verify whether this connection is after all allowed to connect */
+ if (!silc_server_connection_allowed(server, sock,
+ entry->conn_type,
+ &server->config->param,
+ rconn ? rconn->param : NULL,
+ silc_connauth_get_ske(connauth))) {
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ server->stat.auth_failures++;
+ goto out;
+ }
+
+ if (rconn) {
+ if (rconn->param) {
+ 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;
+ backup_local = rconn->backup_local;
+ backup_router = rconn->backup_router;
+ backup_replace_ip = rconn->backup_replace_ip;
+ backup_replace_port = rconn->backup_replace_port;
+ }
+ }
+
+ if (entry->conn_type == SILC_CONN_SERVER) {
+ /* Verify whether this connection is after all allowed to connect */
+ if (!silc_server_connection_allowed(server, sock,
+ entry->conn_type,
+ &server->config->param,
+ srvconn ? srvconn->param : NULL,
+ silc_connauth_get_ske(connauth))) {
+ server->stat.auth_failures++;
+ goto out;
+ }
+ if (srvconn) {
+ if (srvconn->param) {
+ param = srvconn->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 = srvconn->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,
+ ip) &&
+ silc_server_find_socket_by_host(server,
+ SILC_CONN_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_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+
+ /* From here on, wait 20 seconds for the backup router to appear. */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_router_wait,
+ (void *)server, 20, 0);
+ goto out;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Remote host is %s",
+ entry->conn_type == SILC_CONN_SERVER ?
+ "server" : (backup_router ?
+ "backup router" : "router")));
+ SILC_LOG_INFO(("Connection %s (%s) is %s", entry->hostname,
+ entry->ip, entry->conn_type == SILC_CONN_SERVER ?
+ "server" : (backup_router ?
+ "backup router" : "router")));
+
+ /* Add the server into server cache. The server name and Server ID
+ is updated after we have received NEW_SERVER packet from the
+ server. We mark ourselves as router for this server if we really
+ are router. */
+ new_server =
+ silc_idlist_add_server((entry->conn_type == SILC_CONN_SERVER ?
+ server->local_list : (backup_router ?
+ server->local_list :
+ server->global_list)),
+ NULL,
+ (entry->conn_type == SILC_CONN_SERVER ?
+ SILC_SERVER : SILC_ROUTER),
+ NULL,
+ (entry->conn_type == SILC_CONN_SERVER ?
+ server->id_entry : (backup_router ?
+ server->id_entry : NULL)),
+ sock);
+ if (!new_server) {
+ SILC_LOG_ERROR(("Could not add new server to cache"));
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ server->stat.auth_failures++;
+ goto out;
+ }
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
+ entry->data.conn_type = entry->conn_type;
+
+ 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 (entry->data.conn_type == SILC_CONN_ROUTER && backup_router) {
+ /* Change it back to SERVER type since that's what it really is. */
+ if (backup_local)
+ entry->data.conn_type = SILC_CONN_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",
+ entry->hostname));
+
+ /* Remove the backup waiting with timeout */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_router_wait,
+ (void *)server, 10, 0);
+ }
+
+ /* Statistics */
+ if (entry->data.conn_type == SILC_CONN_SERVER) {
+ server->stat.my_servers++;
+ server->stat.servers++;
+ SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+ } else {
+ server->stat.my_routers++;
+ server->stat.routers++;
+ SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+ }
+
+ /* Check whether this connection is to be our primary router connection
+ if we do not already have the primary route. */
+ if (!backup_router &&
+ server->standalone && entry->data.conn_type == SILC_CONN_ROUTER) {
+ if (silc_server_config_is_primary_route(server) && !initiator)
+ break;
+
+ SILC_LOG_DEBUG(("We are not standalone server anymore"));
+ server->standalone = FALSE;
+ if (!server->id_entry->router) {
+ server->id_entry->router = id_entry;
+ server->router = id_entry;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ goto out;
break;
+ }
+
+ /* Add connection to server->conns so that we know we have connection
+ to this peer. */
+ sconn = silc_calloc(1, sizeof(*sconn));
+ sconn->server = server;
+ sconn->sock = sock;
+ sconn->remote_host = strdup(hostname);
+ sconn->remote_port = port;
+ silc_dlist_add(server->conns, sconn);
+ idata->sconn = sconn;
+ idata->last_receive = time(NULL);
+
+ /* Add the common data structure to the ID entry. */
+ silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+ silc_packet_set_context(sock, id_entry);
+
+ /* Connection has been fully established now. Everything is ok. */
+ SILC_LOG_DEBUG(("New connection %p authenticated", sconn));
+
+ /* Perform Quality of Service */
+ if (param->qos)
+ silc_socket_stream_set_qos(silc_packet_stream_get_stream(sock),
+ param->qos_rate_limit, param->qos_bytes_limit,
+ param->qos_limit_sec, param->qos_limit_usec);
+
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_free(entry);
+
+ out:
+ silc_ske_free(silc_connauth_get_ske(connauth));
+ silc_connauth_free(connauth);
+}
+
+/* SKE completion callback. We set the new keys into use here. */
+
+static void
+silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
+ SilcSKESecurityProperties prop,
+ SilcSKEKeyMaterial keymat,
+ SilcSKERekeyMaterial rekey,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sock);
+ SilcIDListData idata = (SilcIDListData)entry;
+ SilcServer server = entry->server;
+ SilcConnAuth connauth;
+ SilcCipher send_key, receive_key;
+ SilcHmac hmac_send, hmac_receive;
+ SilcHash hash;
+ unsigned char *pk;
+ SilcUInt32 pk_len;
+
+ entry->op = NULL;
+
+ if (status != SILC_SKE_STATUS_OK) {
+ /* SKE failed */
+ SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
+ silc_ske_map_status(status), entry->hostname, entry->ip));
+ silc_ske_free(ske);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Setting keys into use"));
+
+ /* Set the keys into use. The data will be encrypted after this. */
+ if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
+ &hmac_send, &hmac_receive, &hash)) {
+ /* Error setting keys */
+ silc_ske_free(ske);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+ silc_packet_set_keys(sock, send_key, receive_key, hmac_send,
+ hmac_receive, FALSE);
+
+ idata->rekey = rekey;
+ idata->public_key = silc_pkcs_public_key_copy(prop->public_key);
+ pk = silc_pkcs_public_key_encode(idata->public_key, &pk_len);
+ silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
+
+ silc_hash_alloc(silc_hash_get_name(prop->hash), &idata->hash);
+
+ SILC_LOG_DEBUG(("Starting connection authentication"));
+ server->stat.auth_attempts++;
+
+ connauth = silc_connauth_alloc(server->schedule, ske,
+ server->config->conn_auth_timeout);
+ if (!connauth) {
+ /** Error allocating auth protocol */
+ silc_ske_free(ske);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ silc_server_config_unref(&entry->cconfig);
+ silc_server_config_unref(&entry->sconfig);
+ silc_server_config_unref(&entry->rconfig);
+ silc_server_free_sock_user_data(server, sock, NULL);
+ return;
+ }
+
+ /* Start connection authentication */
+ entry->op =
+ silc_connauth_responder(connauth, silc_server_accept_get_auth,
+ silc_server_accept_auth_compl, sock);
+}
+
+/* Accept new TCP connection */
+
+static void silc_server_accept_new_connection(SilcNetStatus status,
+ SilcStream stream,
+ void *context)
+{
+ SilcServer server = context;
+ SilcPacketStream packet_stream;
+ SilcServerConfigClient *cconfig = NULL;
+ SilcServerConfigServer *sconfig = NULL;
+ SilcServerConfigRouter *rconfig = NULL;
+ SilcServerConfigDeny *deny;
+ SilcUnknownEntry entry;
+ SilcSKE ske;
+ SilcSKEParamsStruct params;
+ char *hostname, *ip;
+ SilcUInt16 port;
+
+ SILC_LOG_DEBUG(("Accepting new connection"));
+
+ /* Check for maximum allowed connections */
+ server->stat.conn_attempts++;
+ if (silc_dlist_count(server->conns) >
+ server->config->param.connections_max) {
+ SILC_LOG_ERROR(("Refusing connection, server is full"));
+ server->stat.conn_failures++;
+ silc_stream_destroy(stream);
+ return;
+ }
+
+ /* Get hostname, IP and port */
+ if (!silc_socket_stream_get_info(stream, NULL, (const char **)&hostname,
+ (const char **)&ip, &port)) {
+ /* Bad socket stream */
+ server->stat.conn_failures++;
+ silc_stream_destroy(stream);
+ return;
+ }
+
+ /* Create packet stream */
+ packet_stream = silc_packet_stream_create(server->packet_engine,
+ server->schedule, stream);
+ if (!packet_stream) {
+ SILC_LOG_ERROR(("Refusing connection, cannot create packet stream"));
+ server->stat.conn_failures++;
+ silc_stream_destroy(stream);
+ return;
+ }
+ server->stat.conn_num++;
+
+ SILC_LOG_DEBUG(("Created packet stream %p", packet_stream));
+
+ /* Set source ID to packet stream */
+ if (!silc_packet_set_ids(packet_stream, SILC_ID_SERVER, server->id,
+ 0, NULL)) {
+ /* Out of memory */
+ server->stat.conn_failures++;
+ silc_packet_stream_destroy(packet_stream);
+ return;
+ }
+
+ /* Check whether this connection is denied to connect to us. */
+ deny = silc_server_config_find_denied(server, ip);
+ if (!deny)
+ deny = silc_server_config_find_denied(server, hostname);
+ if (deny) {
+ /* The connection is denied */
+ SILC_LOG_INFO(("Connection %s (%s) is denied", hostname, ip));
+ silc_server_disconnect_remote(server, packet_stream,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER,
+ deny->reason);
+ silc_server_free_sock_user_data(server, packet_stream, NULL);
+ return;
+ }
+
+ /* Check whether we have configured this sort of connection at all. We
+ have to check all configurations since we don't know what type of
+ connection this is. */
+ if (!(cconfig = silc_server_config_find_client(server, ip)))
+ cconfig = silc_server_config_find_client(server, hostname);
+ if (!(sconfig = silc_server_config_find_server_conn(server, ip)))
+ sconfig = silc_server_config_find_server_conn(server, hostname);
+ if (server->server_type == SILC_ROUTER)
+ if (!(rconfig = silc_server_config_find_router_conn(server, ip, port)))
+ rconfig = silc_server_config_find_router_conn(server, hostname, port);
+ if (!cconfig && !sconfig && !rconfig) {
+ SILC_LOG_INFO(("Connection %s (%s) is not allowed", hostname, ip));
+ server->stat.conn_failures++;
+ silc_server_disconnect_remote(server, packet_stream,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER, NULL);
+ silc_server_free_sock_user_data(server, packet_stream, NULL);
+ return;
+ }
+
+ /* The connection is allowed */
+ entry = silc_calloc(1, sizeof(*entry));
+ if (!entry) {
+ server->stat.conn_failures++;
+ silc_server_disconnect_remote(server, packet_stream,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ silc_server_free_sock_user_data(server, packet_stream, NULL);
+ return;
+ }
+ entry->hostname = hostname;
+ entry->ip = ip;
+ entry->port = port;
+ entry->server = server;
+ entry->data.conn_type = SILC_CONN_UNKNOWN;
+ silc_packet_set_context(packet_stream, entry);
+
+ SILC_LOG_DEBUG(("Created unknown connection %p", entry));
+
+ silc_server_config_ref(&entry->cconfig, server->config, cconfig);
+ silc_server_config_ref(&entry->sconfig, server->config, sconfig);
+ silc_server_config_ref(&entry->rconfig, server->config, rconfig);
+
+ /* Take flags for key exchange. Since we do not know what type of connection
+ this is, we go through all found configurations and use the global ones
+ as well. This will result always into strictest key exchange flags. */
+ memset(¶ms, 0, sizeof(params));
+ SILC_GET_SKE_FLAGS(cconfig, params.flags);
+ SILC_GET_SKE_FLAGS(sconfig, params.flags);
+ SILC_GET_SKE_FLAGS(rconfig, params.flags);
+ if (server->config->param.key_exchange_pfs)
+ params.flags |= SILC_SKE_SP_FLAG_PFS;
+
+ SILC_LOG_INFO(("Incoming connection %s (%s)", hostname, ip));
+ server->stat.conn_attempts++;
+
+ /* Start SILC Key Exchange protocol */
+ SILC_LOG_DEBUG(("Starting key exchange protocol"));
+ ske = silc_ske_alloc(server->rng, server->schedule, server->repository,
+ server->public_key, server->private_key,
+ packet_stream);
+ if (!ske) {
+ server->stat.conn_failures++;
+ silc_server_disconnect_remote(server, packet_stream,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ silc_server_free_sock_user_data(server, packet_stream, NULL);
+ return;
+ }
+ silc_ske_set_callbacks(ske, silc_server_verify_key,
+ silc_server_accept_completed, packet_stream);
+
+ /* Start key exchange protocol */
+ params.version = silc_version_string;
+ params.timeout_secs = server->config->key_exchange_timeout;
+ entry->op = silc_ske_responder(ske, packet_stream, ¶ms);
+}
+
+
+/********************************** Rekey ***********************************/
+
+/* Initiator rekey completion callback */
+
+static void silc_server_rekey_completion(SilcSKE ske,
+ SilcSKEStatus status,
+ const SilcSKESecurityProperties prop,
+ const SilcSKEKeyMaterial keymat,
+ SilcSKERekeyMaterial rekey,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcIDListData idata = silc_packet_get_context(sock);
+ SilcServer server = idata->sconn->server;
+
+ idata->sconn->op = NULL;
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_ERROR(("Error during rekey protocol with %s",
+ idata->sconn->remote_host));
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]",
+ idata->sconn->remote_host, idata->sconn->remote_port,
+ SILC_CONNTYPE_STRING(idata->conn_type)));
+
+ /* Save rekey data for next rekey */
+ idata->rekey = rekey;
+
+ /* Register new rekey timeout */
+ silc_schedule_task_add_timeout(server->schedule, silc_server_do_rekey,
+ sock, idata->sconn->rekey_timeout, 0);
+}
+
+/* Rekey callback. Start rekey as initiator */
+
+SILC_TASK_CALLBACK(silc_server_do_rekey)
+{
+ SilcServer server = app_context;
+ SilcPacketStream sock = context;
+ SilcIDListData idata = silc_packet_get_context(sock);
+ SilcSKE ske;
+
+ SILC_LOG_DEBUG(("Perform rekey, sock %p", sock));
+
+ /* Do not execute rekey with disabled connections */
+ if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+ return;
- case SILC_PACKET_HEARTBEAT:
- /*
- * Received heartbeat.
- */
- SILC_LOG_DEBUG(("Heartbeat packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- break;
+ /* If another protocol is active do not start rekey */
+ if (idata->sconn->op) {
+ SILC_LOG_DEBUG(("Waiting for other protocol to finish before rekeying"));
+ silc_schedule_task_add_timeout(server->schedule, silc_server_do_rekey,
+ sock, 60, 0);
+ return;
+ }
- case SILC_PACKET_KEY_AGREEMENT:
- /*
- * Received heartbeat.
- */
- SILC_LOG_DEBUG(("Key agreement packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- silc_server_key_agreement(server, sock, packet);
- break;
+ SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s]",
+ idata->sconn->remote_host, idata->sconn->remote_port,
+ SILC_CONNTYPE_STRING(idata->conn_type)));
- default:
- SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
- break;
+ /* Allocate SKE */
+ ske = silc_ske_alloc(server->rng, server->schedule, NULL,
+ server->public_key, NULL, sock);
+ if (!ske)
+ return;
+
+ /* Set SKE callbacks */
+ silc_ske_set_callbacks(ske, NULL, silc_server_rekey_completion, sock);
+
+ /* Perform rekey */
+ idata->sconn->op = silc_ske_rekey_initiator(ske, sock, idata->rekey);
+}
+
+/* Responder rekey completion callback */
+
+static void
+silc_server_rekey_resp_completion(SilcSKE ske,
+ SilcSKEStatus status,
+ const SilcSKESecurityProperties prop,
+ const SilcSKEKeyMaterial keymat,
+ SilcSKERekeyMaterial rekey,
+ void *context)
+{
+ SilcPacketStream sock = context;
+ SilcIDListData idata = silc_packet_get_context(sock);
+
+ idata->sconn->op = NULL;
+ if (status != SILC_SKE_STATUS_OK) {
+ SILC_LOG_ERROR(("Error during rekey protocol with %s",
+ idata->sconn->remote_host));
+ return;
}
-
+
+ SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]",
+ idata->sconn->remote_host, idata->sconn->remote_port,
+ SILC_CONNTYPE_STRING(idata->conn_type)));
+
+ /* Save rekey data for next rekey */
+ idata->rekey = rekey;
}
-/* Creates connection to a remote router. */
+/* Start rekey as responder */
-void silc_server_create_connection(SilcServer server,
- char *remote_host, uint32 port)
+static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
+ SilcPacket packet)
{
- SilcServerConnection sconn;
+ SilcIDListData idata = silc_packet_get_context(sock);
+ SilcSKE ske;
+
+ SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s], sock %p",
+ idata->sconn->remote_host, idata->sconn->remote_port,
+ SILC_CONNTYPE_STRING(idata->conn_type), sock));
+
+ /* Allocate SKE */
+ ske = silc_ske_alloc(server->rng, server->schedule, NULL,
+ server->public_key, NULL, sock);
+ if (!ske) {
+ silc_packet_free(packet);
+ return;
+ }
- /* 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;
+ /* Set SKE callbacks */
+ silc_ske_set_callbacks(ske, NULL, silc_server_rekey_resp_completion, sock);
- silc_task_register(server->timeout_queue, 0,
- silc_server_connect_router,
- (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ /* Perform rekey */
+ idata->sconn->op = silc_ske_rekey_responder(ske, sock, idata->rekey,
+ packet);
}
+
+/****************************** Disconnection *******************************/
+
+/* Destroys packet stream. */
+
SILC_TASK_CALLBACK(silc_server_close_connection_final)
{
- silc_socket_free((SilcSocketConnection)context);
+ silc_packet_stream_unref(context);
}
/* Closes connection to socket connection */
void silc_server_close_connection(SilcServer server,
- SilcSocketConnection sock)
+ SilcPacketStream sock)
{
- SILC_LOG_DEBUG(("Closing connection %d", sock->sock));
+ SilcIDListData idata = silc_packet_get_context(sock);
+ char tmp[128];
+ const char *hostname;
+ SilcUInt16 port;
+
+ if (!silc_packet_stream_is_valid(sock))
+ return;
- /* We won't listen for this connection anymore */
- silc_schedule_unset_listen_fd(sock->sock);
+ memset(tmp, 0, sizeof(tmp));
+ // silc_socket_get_error(sock, tmp, sizeof(tmp));
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, NULL, &port);
+ SILC_LOG_INFO(("Closing connection %s:%d [%s] %s", hostname, port,
+ idata ? SILC_CONNTYPE_STRING(idata->conn_type) : "",
+ tmp[0] ? tmp : ""));
- /* Unregister all tasks */
- silc_task_unregister_by_fd(server->io_queue, sock->sock);
- silc_task_unregister_by_fd(server->timeout_queue, sock->sock);
+ // silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
- /* Close the actual connection */
- silc_net_close_connection(sock->sock);
- server->sockets[sock->sock] = NULL;
+ if (idata && idata->sconn) {
+ silc_server_connection_free(idata->sconn);
+ idata->sconn = NULL;
+ }
- silc_task_register(server->timeout_queue, 0,
- silc_server_close_connection_final,
- (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ /* Take a reference and then destroy the stream. The last reference
+ is released later in a timeout callback. */
+ silc_packet_stream_ref(sock);
+ silc_packet_stream_destroy(sock);
+
+ /* Close connection with timeout */
+ server->stat.conn_num--;
+ silc_schedule_task_del_by_all(server->schedule, 0,
+ silc_server_close_connection_final, sock);
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_close_connection_final,
+ sock, 0, 1);
}
-/* Sends disconnect message to remote connection and disconnects the
+/* Sends disconnect message to remote connection and disconnects the
connection. */
void silc_server_disconnect_remote(SilcServer server,
- SilcSocketConnection sock,
- const char *fmt, ...)
+ SilcPacketStream sock,
+ SilcStatus status, ...)
{
+ unsigned char buf[512];
va_list ap;
- unsigned char buf[4096];
+ char *cp;
if (!sock)
return;
- memset(buf, 0, sizeof(buf));
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
- va_end(ap);
-
- SILC_LOG_DEBUG(("Disconnecting remote host"));
+ SILC_LOG_DEBUG(("Disconnecting remote host, sock %p", sock));
- SILC_LOG_INFO(("Disconnecting %s:%d [%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")));
+ va_start(ap, status);
+ cp = va_arg(ap, char *);
+ if (cp)
+ silc_vsnprintf(buf, sizeof(buf), cp, ap);
+ va_end(ap);
- /* Notify remote end that the conversation is over. The notify message
- is tried to be sent immediately. */
- silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,
- buf, strlen(buf), TRUE);
+ /* Send SILC_PACKET_DISCONNECT */
+ silc_packet_send_va(sock, SILC_PACKET_DISCONNECT, 0,
+ SILC_STR_UI_CHAR(status),
+ SILC_STR_UI8_STRING(cp ? buf : NULL),
+ SILC_STR_END);
- /* Mark the connection to be disconnected */
- SILC_SET_DISCONNECTED(sock);
+ /* Close connection */
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;
-
- silc_idlist_del_data(i->client);
- silc_idcache_purge_by_context(i->server->local_list->clients, i->client);
- silc_free(i);
-}
-
/* Frees client data and notifies about client's signoff. */
-void silc_server_free_client_data(SilcServer server,
- SilcSocketConnection sock,
- SilcClientEntry client,
+void silc_server_free_client_data(SilcServer server,
+ SilcPacketStream sock,
+ SilcClientEntry client,
int notify,
- char *signoff)
+ const char *signoff)
{
- FreeClientInternal i = silc_calloc(1, sizeof(*i));
-
- /* If there is pending outgoing data for the client then purge it
- to the network before removing the client entry. */
- if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
- (SILC_IS_DISCONNECTED(sock) == FALSE)) {
- server->stat.packets_sent++;
-
- if (sock->outbuf->data - sock->outbuf->head)
- silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+ SILC_LOG_DEBUG(("Freeing client %p data", client));
- silc_server_packet_send_real(server, sock, TRUE);
+ if (client->id) {
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_SIGNOFF);
- SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
- SILC_UNSET_OUTBUF_PENDING(sock);
- silc_buffer_clear(sock->outbuf);
+ /* Send SIGNOFF notify to routers. */
+ if (notify)
+ silc_server_send_notify_signoff(server, SILC_PRIMARY_ROUTE(server),
+ SILC_BROADCAST(server), client->id,
+ 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,
- SILC_ID_CLIENT_LEN, signoff);
-
/* Remove client from all channels */
if (notify)
- silc_server_remove_from_channels(server, NULL, client,
- TRUE, signoff, TRUE);
+ 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);
+ silc_server_remove_from_channels(server, NULL, client,
+ FALSE, NULL, FALSE, FALSE);
- /* 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_task_register(server->timeout_queue, 0,
- silc_server_free_client_data_timeout,
- (void *)i, 300, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
- client->data.registered = FALSE;
-
- /* Free the client entry and everything in it */
+ /* Remove this client from watcher list if it is */
+ silc_server_del_from_watcher_list(server, client);
+
+ /* Remove client's public key from repository, this will free it too. */
+ if (client->data.public_key) {
+ silc_skr_del_public_key(server->repository, client->data.public_key,
+ client);
+ client->data.public_key = NULL;
+ }
+
+ /* 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);
+ silc_schedule_task_del_by_context(server->schedule, client);
+
+ if (client->data.sconn) {
+ silc_server_connection_free(client->data.sconn);
+ client->data.sconn = NULL;
+ }
+
+ /* We will not delete the client entry right away. We will take it
+ into history (for WHOWAS command) for 5 minutes, unless we're
+ shutting down server. */
+ if (!server->server_shutdown) {
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+ client->mode = 0;
+ client->router = NULL;
+ client->connection = NULL;
+ client->data.created = silc_time();
+ silc_dlist_del(server->expired_clients, client);
+ silc_dlist_add(server->expired_clients, client);
+ } else {
+ /* Delete directly since we're shutting down server */
+ SILC_LOG_DEBUG(("Delete client directly"));
+ silc_idlist_del_data(client);
+ silc_idlist_del_client(server->local_list, client);
+ }
}
/* Frees user_data pointer from socket connection object. This also sends
appropriate notify packets to the network to inform about leaving
entities. */
-void silc_server_free_sock_user_data(SilcServer server,
- SilcSocketConnection sock)
+void silc_server_free_sock_user_data(SilcServer server,
+ SilcPacketStream sock,
+ const char *signoff_message)
{
- SILC_LOG_DEBUG(("Start"));
+ SilcIDListData idata;
+ const char *ip;
+ SilcUInt16 port;
- switch(sock->type) {
- case SILC_SOCKET_TYPE_CLIENT:
- {
- SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
- silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
- break;
- }
- case SILC_SOCKET_TYPE_SERVER:
- case SILC_SOCKET_TYPE_ROUTER:
- {
- SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
+ if (!sock)
+ return;
- /* Free all client entries that this server owns as they will
- become invalid now as well. */
- silc_server_remove_clients_by_server(server, user_data, TRUE);
+ SILC_LOG_DEBUG(("Start, sock %p", sock));
- /* If this was our primary router connection then we're lost to
- the outside world. */
- if (server->router == user_data) {
- server->id_entry->router = NULL;
- server->router = NULL;
- server->standalone = TRUE;
- }
+ idata = silc_packet_get_context(sock);
+ if (!idata)
+ return;
- /* Free the server entry */
- silc_idlist_del_data(user_data);
- silc_idlist_del_server(server->local_list, user_data);
- server->stat.my_servers--;
- server->stat.servers--;
- if (server->server_type == SILC_ROUTER)
- server->stat.cell_servers--;
- break;
- }
- default:
- {
- SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data;
+ silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_rekey,
+ sock);
- silc_idlist_del_data(user_data);
- silc_free(user_data);
- break;
+ /* Cancel active protocols */
+ if (idata) {
+ if (idata->sconn && idata->sconn->op) {
+ SILC_LOG_DEBUG(("Abort active protocol"));
+ silc_async_abort(idata->sconn->op, NULL, NULL);
+ }
+ if (idata->conn_type == SILC_CONN_UNKNOWN &&
+ ((SilcUnknownEntry)idata)->op) {
+ SILC_LOG_DEBUG(("Abort active protocol"));
+ silc_async_abort(((SilcUnknownEntry)idata)->op, NULL, NULL);
}
}
- sock->user_data = NULL;
-}
+ switch (idata->conn_type) {
+ case SILC_CONN_CLIENT:
+ {
+ SilcClientEntry client_entry = (SilcClientEntry)idata;
+ silc_server_free_client_data(server, sock, client_entry, TRUE,
+ signoff_message);
+ silc_packet_set_context(sock, NULL);
+ break;
+ }
-/* This function is used to remove all client entries by the server `entry'.
- This is called when the connection is lost to the server. In this case
- we must invalidate all the client entries owned by the server `entry'.
- If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is
- distributed to our local clients. */
+ case SILC_CONN_SERVER:
+ case SILC_CONN_ROUTER:
+ {
+ SilcServerEntry user_data = (SilcServerEntry)idata;
+ SilcServerEntry backup_router = NULL;
-int silc_server_remove_clients_by_server(SilcServer server,
- SilcServerEntry entry,
- int server_signoff)
-{
- SilcIDCacheList list = NULL;
- SilcIDCacheEntry id_cache = NULL;
- SilcClientEntry client = NULL;
- SilcBuffer idp;
- SilcClientEntry *clients = NULL;
- uint32 clients_c = 0;
- unsigned char **argv = NULL;
- uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
- int i;
+ SILC_LOG_DEBUG(("Freeing server %p data", user_data));
- SILC_LOG_DEBUG(("Start"));
+ if (user_data->id)
+ backup_router = silc_server_backup_get(server, user_data->id);
- if (server_signoff) {
- idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
- argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
- argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
- argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
- argv[argc] = idp->data;
- argv_lens[argc] = idp->len;
- argv_types[argc] = argc + 1;
- argc++;
- silc_buffer_free(idp);
- }
+ if (!server->backup_router && server->server_type == SILC_ROUTER &&
+ backup_router == server->id_entry &&
+ idata->conn_type != SILC_CONN_ROUTER)
+ backup_router = NULL;
- if (silc_idcache_find_by_id(server->local_list->clients,
- SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+ if (server->server_shutdown || server->backup_noswitch)
+ backup_router = NULL;
- if (silc_idcache_list_first(list, &id_cache)) {
- while (id_cache) {
- client = (SilcClientEntry)id_cache->context;
- if (client->data.registered == FALSE) {
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
- }
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, NULL, &ip, &port);
- if (client->router != entry) {
- if (server_signoff && client->connection) {
- clients = silc_realloc(clients,
- sizeof(*clients) * (clients_c + 1));
- clients[clients_c] = client;
- clients_c++;
+ /* If this was our primary router connection then we're lost to
+ the outside world. */
+ if (server->router == user_data) {
+ /* Check whether we have a backup router connection */
+ if (!backup_router || backup_router == user_data) {
+ if (!server->no_reconnect)
+ silc_server_create_connections(server);
+ server->id_entry->router = NULL;
+ server->router = NULL;
+ server->standalone = TRUE;
+ server->backup_primary = FALSE;
+ backup_router = NULL;
+ } else {
+ if (server->id_entry != backup_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;
+ backup_router->data.status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+ /* Send START_USE to backup router to indicate we have switched */
+ silc_server_backup_send_start_use(server,
+ backup_router->connection,
+ FALSE);
+ } else {
+ SILC_LOG_INFO(("We are now new primary router in this cell"));
+ server->id_entry->router = NULL;
+ server->router = NULL;
+ server->standalone = TRUE;
}
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
- }
-
- if (server_signoff) {
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
- argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
- (argc + 1));
- argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
- (argc + 1));
- argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
- memcpy(argv[argc], idp->data, idp->len);
- argv_lens[argc] = idp->len;
- argv_types[argc] = argc + 1;
- argc++;
- silc_buffer_free(idp);
- }
-
- /* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, FALSE,
- NULL, FALSE);
- silc_idlist_del_client(server->local_list, client);
+ /* We stop here to take a breath */
+ sleep(2);
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- }
- }
- silc_idcache_list_free(list);
- }
-
- if (silc_idcache_find_by_id(server->global_list->clients,
- SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
+ if (server->backup_router) {
+ server->server_type = SILC_ROUTER;
- if (silc_idcache_list_first(list, &id_cache)) {
- while (id_cache) {
- client = (SilcClientEntry)id_cache->context;
- if (client->data.registered == FALSE) {
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
- }
-
- if (client->router != entry) {
- if (server_signoff && client->connection) {
- clients = silc_realloc(clients,
- sizeof(*clients) * (clients_c + 1));
- clients[clients_c] = client;
- clients_c++;
+ /* We'll need to constantly try to reconnect to the primary
+ router so that we'll see when it comes back online. */
+ silc_server_create_connection(server, TRUE, FALSE, ip, port,
+ silc_server_backup_connected,
+ NULL);
}
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- else
- continue;
+ /* Mark this connection as replaced */
+ silc_server_backup_replaced_add(server, user_data->id,
+ backup_router);
}
+ } else if (backup_router) {
+ SILC_LOG_INFO(("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 &&
+ idata->conn_type == SILC_CONN_ROUTER) {
+ /* Reconnect to the router (backup) */
+ if (!server->no_reconnect)
+ silc_server_create_connections(server);
+ }
- if (server_signoff) {
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
- argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
- argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) *
- (argc + 1));
- argv_types = silc_realloc(argv_types, sizeof(*argv_types) *
- (argc + 1));
- argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
- memcpy(argv[argc], idp->data, idp->len);
- argv_lens[argc] = idp->len;
- argv_types[argc] = argc + 1;
- argc++;
- silc_buffer_free(idp);
- }
+ if (user_data->server_name)
+ SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE,
+ ("Server %s signoff", user_data->server_name));
+
+ if (!backup_router) {
+ /* 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);
+
+#if 0
+ /* 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_CONN_SERVER)
+ silc_server_remove_clients_by_server(server, server->router,
+ user_data, TRUE);
+ else
+#endif
+ silc_server_remove_clients_by_server(server, user_data,
+ user_data, TRUE);
- /* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, FALSE,
- NULL, FALSE);
- silc_idlist_del_client(server->global_list, client);
+ /* 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. 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;
- if (!silc_idcache_list_next(list, &id_cache))
- break;
+ if (idata->sconn) {
+ silc_server_connection_free(idata->sconn);
+ idata->sconn = NULL;
}
- }
- silc_idcache_list_free(list);
- }
- /* Send the SERVER_SIGNOFF notify */
- if (server_signoff) {
- SilcBuffer args;
+ /* Statistics */
+ if (idata->conn_type == SILC_CONN_SERVER) {
+ server->stat.my_servers--;
+ server->stat.servers--;
+ SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+ } else if (idata->conn_type == SILC_CONN_ROUTER) {
+ server->stat.my_routers--;
+ server->stat.routers--;
+ SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+ }
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_servers--;
- /* Send SERVER_SIGNOFF notify to our primary router */
- if (!server->standalone && server->router) {
- args = silc_argument_payload_encode(1, argv, argv_lens,
- argv_types);
- silc_server_send_notify_args(server,
- server->router->connection,
- server->server_type ==
- SILC_SERVER ? FALSE : TRUE,
- SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
- argc, args);
- silc_buffer_free(args);
- }
+ /* Free the server entry */
+ silc_server_backup_del(server, user_data);
+ silc_server_backup_replaced_del(server, user_data);
+ silc_idlist_del_data(user_data);
+ if (!silc_idlist_del_server(server->local_list, user_data))
+ silc_idlist_del_server(server->global_list, user_data);
+
+ 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)
+ silc_server_announce_servers(server, FALSE, time(0) - 300,
+ backup_router->connection);
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, time(0) - 300,
+ backup_router->connection);
+ silc_server_announce_channels(server, time(0) - 300,
+ backup_router->connection);
+ }
- args = silc_argument_payload_encode(argc, argv, argv_lens,
- argv_types);
- /* Send to local clients */
- for (i = 0; i < clients_c; i++) {
- silc_server_send_notify_args(server, clients[i]->connection,
- FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
- argc, args);
+ silc_packet_set_context(sock, NULL);
+ break;
}
- silc_free(clients);
- silc_buffer_free(args);
- silc_free(argv);
- silc_free(argv_lens);
- silc_free(argv_types);
- }
-
- return TRUE;
-}
-
-/* Checks whether given channel has global users. If it does this returns
- TRUE and FALSE if there is only locally connected clients on the channel. */
-
-int silc_server_channel_has_global(SilcChannelEntry channel)
-{
- SilcChannelClientEntry chl;
-
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (chl->client->router)
- return TRUE;
- }
-
- return FALSE;
-}
+ default:
+ {
+ SilcUnknownEntry entry = (SilcUnknownEntry)idata;
-/* Checks whether given channel has locally connected users. If it does this
- returns TRUE and FALSE if there is not one locally connected client. */
+ SILC_LOG_DEBUG(("Freeing unknown connection data %p", entry));
-int silc_server_channel_has_local(SilcChannelEntry channel)
-{
- SilcChannelClientEntry chl;
+ if (idata->sconn) {
+ if (server->router_conn == idata->sconn) {
+ if (!server->no_reconnect)
+ silc_server_create_connections(server);
+ server->router_conn = NULL;
+ }
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- if (!chl->client->router)
- return TRUE;
+ silc_server_connection_free(idata->sconn);
+ idata->sconn = NULL;
+ }
+ silc_idlist_del_data(idata);
+ silc_free(entry);
+ silc_packet_set_context(sock, NULL);
+ break;
+ }
}
-
- return FALSE;
}
/* Removes client from all channels it has joined. This is used when client
connection is disconnected. If the client on a channel is last, the
channel is removed as well. This sends the SIGNOFF notify types. */
-void silc_server_remove_from_channels(SilcServer server,
- SilcSocketConnection sock,
+void silc_server_remove_from_channels(SilcServer server,
+ SilcPacketStream sock,
SilcClientEntry client,
- int notify,
- char *signoff_message,
- int keygen)
+ SilcBool notify,
+ const char *signoff_message,
+ SilcBool keygen,
+ SilcBool killed)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
- SilcBuffer clidp;
-
- SILC_LOG_DEBUG(("Start"));
+ SilcHashTableList htl;
+ SilcBuffer clidp = NULL;
- if (!client || !client->id)
+ if (!client)
return;
- clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (notify && !client->id)
+ notify = FALSE;
+
+ SILC_LOG_DEBUG(("Removing client %s from joined channels",
+ notify ? silc_id_render(client->id, SILC_ID_CLIENT) : ""));
+
+ 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_list_start(client->channels);
- while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
- /* Remove channel from client's channel list */
- silc_list_del(client->channels, chl);
-
- /* Remove channel if there is no users anymore */
- if (server->server_type == SILC_ROUTER &&
- silc_list_count(channel->user_list) < 2) {
- server->stat.my_channels--;
-
- if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
-
- if (channel->founder_key) {
- /* The founder auth data exists, do not remove the channel entry */
- SilcChannelClientEntry chl2;
-
- channel->id = NULL;
-
- silc_list_start(channel->user_list);
- while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- silc_list_del(chl2->client->channels, chl2);
- silc_list_del(channel->user_list, chl2);
- silc_free(chl2);
- }
- continue;
- }
-
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
+ /* Remove channel if this is last client leaving the channel, unless
+ the channel is permanent. */
+ if (server->server_type != SILC_SERVER &&
+ silc_hash_table_count(channel->user_list) < 2) {
+ silc_server_channel_delete(server, channel);
continue;
}
- /* Remove client from channel's client list */
- silc_list_del(channel->user_list, chl);
- silc_free(chl);
- server->stat.my_chanclients--;
+ 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. */
- if (server->server_type == SILC_SERVER &&
- !silc_server_channel_has_global(channel))
+ as local channel. Do not check if the removed client is local client. */
+ 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 (SILC_IS_LOCAL(client))
+ server->stat.my_chanclients--;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.cell_chanclients--;
+ server->stat.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. */
+ need the channel entry anymore, we can remove it safely, unless the
+ channel is permanent channel */
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)
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_SIGNOFF,
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message ? 2 : 1,
- clidp->data, clidp->len,
+ clidp->data, silc_buffer_len(clidp),
signoff_message, signoff_message ?
strlen(signoff_message) : 0);
- server->stat.my_channels--;
-
- if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
-
- if (channel->founder_key) {
- /* The founder auth data exists, do not remove the channel entry */
- SilcChannelClientEntry chl2;
-
- channel->id = NULL;
-
- silc_list_start(channel->user_list);
- while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- silc_list_del(chl2->client->channels, chl2);
- silc_list_del(channel->user_list, chl2);
- silc_free(chl2);
- }
- continue;
- }
-
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ silc_server_channel_delete(server, channel);
continue;
}
- /* Send notify to channel about client leaving SILC and thus
- the entire channel. */
+ /* Send notify to channel about client leaving SILC and channel too */
if (notify)
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_SIGNOFF,
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message ? 2 : 1,
- clidp->data, clidp->len,
+ clidp->data, silc_buffer_len(clidp),
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,
+ silc_buffer_len(clidp), 3);
+ iargs = silc_argument_payload_parse(ab->data, silc_buffer_len(ab), 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)) {
- /* Re-generate channel key */
- silc_server_create_channel_key(server, channel, 0);
-
+ if (!silc_server_create_channel_key(server, channel, 0))
+ continue;
+
/* Send the channel key to the channel. The key of course is not sent
to the client who was removed from the channel. */
- silc_server_send_channel_key(server, client->connection, channel,
- server->server_type == SILC_ROUTER ?
+ silc_server_send_channel_key(server, client->connection, channel,
+ server->server_type == SILC_ROUTER ?
FALSE : !server->standalone);
}
}
- silc_buffer_free(clidp);
+ silc_hash_table_list_reset(&htl);
+ if (clidp)
+ silc_buffer_free(clidp);
}
/* Removes client from one channel. This is used for example when client
last client leaves the channel. If `notify' is FALSE notify messages
are not sent. */
-int silc_server_remove_from_one_channel(SilcServer server,
- SilcSocketConnection sock,
- SilcChannelEntry channel,
- SilcClientEntry client,
- int notify)
+SilcBool silc_server_remove_from_one_channel(SilcServer server,
+ SilcPacketStream sock,
+ SilcChannelEntry channel,
+ SilcClientEntry client,
+ SilcBool notify)
{
- SilcChannelEntry ch;
SilcChannelClientEntry chl;
SilcBuffer clidp;
- SILC_LOG_DEBUG(("Start"));
-
- clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
- /* Remove the client from the channel. The client is removed from
- the channel's user list. */
- silc_list_start(client->channels);
- while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
- if (chl->channel != channel)
- continue;
-
- ch = chl->channel;
-
- /* Remove channel from client's channel list */
- silc_list_del(client->channels, chl);
-
- /* Remove channel if there is no users anymore */
- if (server->server_type == SILC_ROUTER &&
- silc_list_count(channel->user_list) < 2) {
- if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
- silc_buffer_free(clidp);
- server->stat.my_channels--;
- return FALSE;
- }
-
- /* Remove client from channel's client list */
- silc_list_del(channel->user_list, chl);
- silc_free(chl);
- server->stat.my_chanclients--;
-
- /* If there is no global users on the channel anymore mark the channel
- as local channel. */
- if (server->server_type == SILC_SERVER &&
- !silc_server_channel_has_global(channel))
- channel->global_users = FALSE;
-
- /* 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. */
- 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)
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_LEAVE, 1,
- clidp->data, clidp->len);
-
- server->stat.my_channels--;
- silc_buffer_free(clidp);
-
- if (channel->rekey)
- silc_task_unregister_by_context(server->timeout_queue, channel->rekey);
-
- if (channel->founder_key) {
- /* The founder auth data exists, do not remove the channel entry */
- SilcChannelClientEntry chl2;
+ SILC_LOG_DEBUG(("Removing %s from channel %s",
+ silc_id_render(client->id, SILC_ID_CLIENT),
+ channel->channel_name));
- channel->id = NULL;
-
- silc_list_start(channel->user_list);
- while ((chl2 = silc_list_get(channel->user_list)) != SILC_LIST_END) {
- silc_list_del(chl2->client->channels, chl2);
- silc_list_del(channel->user_list, chl2);
- silc_free(chl2);
- }
- return FALSE;
- }
-
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
- return FALSE;
- }
+ /* Get the entry to the channel, if this client is not on the channel
+ then return Ok. */
+ if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl))
+ return TRUE;
- /* Send notify to channel about client leaving the channel */
- if (notify)
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_LEAVE, 1,
- clidp->data, clidp->len);
- break;
+ /* Remove channel if this is last client leaving the channel, unless
+ the channel is permanent. */
+ if (server->server_type != SILC_SERVER &&
+ silc_hash_table_count(channel->user_list) < 2) {
+ silc_server_channel_delete(server, channel);
+ return FALSE;
}
- silc_buffer_free(clidp);
- return TRUE;
-}
-
-/* Returns TRUE if the given client is on the channel. FALSE if not.
- This works because we assure that the user list on the channel is
- always in up to date thus we can only check the channel list from
- `client' which is faster than checking the user list from `channel'. */
+ silc_hash_table_del(client->channels, channel);
+ silc_hash_table_del(channel->user_list, client);
+ channel->user_count--;
-int silc_server_client_on_channel(SilcClientEntry client,
- SilcChannelEntry channel)
-{
- SilcChannelClientEntry chl;
-
- if (!client || !channel)
- return FALSE;
+ /* 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_SERVER && channel->global_users &&
+ chl->client->router && !silc_server_channel_has_global(channel))
+ channel->global_users = FALSE;
- silc_list_start(client->channels);
- while ((chl = silc_list_get(client->channels)) != SILC_LIST_END)
- if (chl->channel == channel)
- return TRUE;
-
- return FALSE;
-}
+ memset(chl, 'O', sizeof(*chl));
+ silc_free(chl);
-/* Timeout callback. This is called if connection is idle or for some
- other reason is not responding within some period of time. This
- disconnects the remote end. */
+ /* Update statistics */
+ if (SILC_IS_LOCAL(client))
+ server->stat.my_chanclients--;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.cell_chanclients--;
+ server->stat.chanclients--;
+ }
-SILC_TASK_CALLBACK(silc_server_timeout_remote)
-{
- SilcServer server = (SilcServer)context;
- SilcSocketConnection sock = server->sockets[fd];
+ clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (!clidp)
+ notify = FALSE;
+
+ /* 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_SERVER &&
+ !silc_server_channel_has_local(channel)) {
+ /* Notify about leaving client if this channel has global users. */
+ if (notify && channel->global_users)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_LEAVE, 1,
+ clidp->data, silc_buffer_len(clidp));
- if (!sock)
- return;
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ silc_server_channel_delete(server, channel);
+ silc_buffer_free(clidp);
+ return FALSE;
+ }
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock);
+ /* Send notify to channel about client leaving the channel */
+ if (notify)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
+ SILC_NOTIFY_TYPE_LEAVE, 1,
+ clidp->data, silc_buffer_len(clidp));
- silc_server_disconnect_remote(server, sock,
- "Server closed connection: "
- "Connection timeout");
+ silc_buffer_free(clidp);
+ return TRUE;
}
/* Creates new channel. Sends NEW_CHANNEL packet to primary route. This
are created by routers thus this function is never used by normal
server. */
-SilcChannelEntry silc_server_create_new_channel(SilcServer server,
+SilcChannelEntry silc_server_create_new_channel(SilcServer server,
SilcServerID *router_id,
- char *cipher,
+ char *cipher,
char *hmac,
char *channel_name,
int broadcast)
{
SilcChannelID *channel_id;
SilcChannelEntry entry;
- SilcCipher key;
+ SilcCipher send_key, receive_key;
SilcHmac newhmac;
- SILC_LOG_DEBUG(("Creating new channel"));
+ SILC_LOG_DEBUG(("Creating new channel %s", channel_name));
if (!cipher)
- cipher = "aes-256-cbc";
+ cipher = SILC_DEFAULT_CIPHER;
if (!hmac)
- hmac = "hmac-sha1-96";
+ hmac = SILC_DEFAULT_HMAC;
/* Allocate cipher */
- if (!silc_cipher_alloc(cipher, &key))
+ if (!silc_cipher_alloc(cipher, &send_key))
+ return NULL;
+ if (!silc_cipher_alloc(cipher, &receive_key)) {
+ silc_cipher_free(send_key);
return NULL;
+ }
/* Allocate hmac */
if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
- silc_cipher_free(key);
+ silc_cipher_free(send_key);
+ silc_cipher_free(receive_key);
return NULL;
}
channel_name = strdup(channel_name);
+ /* Create the channel ID */
+ if (!silc_id_create_channel_id(server, router_id, server->rng,
+ &channel_id)) {
+ silc_free(channel_name);
+ silc_cipher_free(send_key);
+ silc_cipher_free(receive_key);
+ silc_hmac_free(newhmac);
+ return NULL;
+ }
+
/* Create the channel */
- silc_id_create_channel_id(router_id, server->rng, &channel_id);
- entry = silc_idlist_add_channel(server->local_list, channel_name,
- SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, newhmac);
+ entry = silc_idlist_add_channel(server->local_list, channel_name,
+ SILC_CHANNEL_MODE_NONE, channel_id,
+ NULL, send_key, receive_key, newhmac);
if (!entry) {
silc_free(channel_name);
+ silc_cipher_free(send_key);
+ silc_cipher_free(receive_key);
+ silc_hmac_free(newhmac);
+ silc_free(channel_id);
return NULL;
}
entry->hmac_name = strdup(hmac);
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry,
- silc_cipher_get_key_len(key) / 8);
+ if (!silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(send_key) / 8)) {
+ silc_idlist_del_channel(server->local_list, entry);
+ return NULL;
+ }
/* 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,
- channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+ 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);
+ /* Distribute to backup routers */
+ if (broadcast && server->server_type == SILC_ROUTER) {
+ SilcBuffer packet;
+ unsigned char cid[32];
+ SilcUInt32 name_len = strlen(channel_name);
+ SilcUInt32 id_len;
+
+ silc_id_id2str(entry->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
+ packet = silc_channel_payload_encode(channel_name, name_len,
+ cid, id_len, entry->mode);
+ silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+ packet->data, silc_buffer_len(packet), FALSE,
+ TRUE);
+ silc_buffer_free(packet);
+ }
+
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.channels++;
+ server->stat.cell_channels++;
+ entry->users_resolved = TRUE;
+ }
return entry;
}
/* Same as above but creates the channel with Channel ID `channel_id. */
-SilcChannelEntry
-silc_server_create_new_channel_with_id(SilcServer server,
- char *cipher,
+SilcChannelEntry
+silc_server_create_new_channel_with_id(SilcServer server,
+ char *cipher,
char *hmac,
char *channel_name,
SilcChannelID *channel_id,
int broadcast)
{
SilcChannelEntry entry;
- SilcCipher key;
+ SilcCipher send_key, receive_key;
SilcHmac newhmac;
- SILC_LOG_DEBUG(("Creating new channel"));
+ SILC_LOG_DEBUG(("Creating new channel %s", channel_name));
if (!cipher)
- cipher = "aes-256-cbc";
+ cipher = SILC_DEFAULT_CIPHER;
if (!hmac)
- hmac = "hmac-sha1-96";
+ hmac = SILC_DEFAULT_HMAC;
/* Allocate cipher */
- if (!silc_cipher_alloc(cipher, &key))
+ if (!silc_cipher_alloc(cipher, &send_key))
+ return NULL;
+ if (!silc_cipher_alloc(cipher, &receive_key)) {
+ silc_cipher_free(send_key);
return NULL;
+ }
/* Allocate hmac */
if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
- silc_cipher_free(key);
+ silc_cipher_free(send_key);
+ silc_cipher_free(receive_key);
return NULL;
}
channel_name = strdup(channel_name);
/* Create the channel */
- entry = silc_idlist_add_channel(server->local_list, channel_name,
- SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, newhmac);
+ entry = silc_idlist_add_channel(server->local_list, channel_name,
+ SILC_CHANNEL_MODE_NONE, channel_id,
+ NULL, send_key, receive_key, newhmac);
if (!entry) {
+ silc_cipher_free(send_key);
+ silc_cipher_free(receive_key);
+ silc_hmac_free(newhmac);
silc_free(channel_name);
return NULL;
}
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry,
- silc_cipher_get_key_len(key) / 8);
+ if (!silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(send_key) / 8)) {
+ silc_idlist_del_channel(server->local_list, entry);
+ return NULL;
+ }
/* 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,
- channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+ 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);
+ /* Distribute to backup routers */
+ if (broadcast && server->server_type == SILC_ROUTER) {
+ SilcBuffer packet;
+ unsigned char cid[32];
+ SilcUInt32 name_len = strlen(channel_name);
+ SilcUInt32 id_len;
+
+ silc_id_id2str(entry->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
+ packet = silc_channel_payload_encode(channel_name, name_len,
+ cid, id_len, entry->mode);
+ silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+ packet->data, silc_buffer_len(packet), FALSE,
+ TRUE);
+ silc_buffer_free(packet);
+ }
+
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.channels++;
+ server->stat.cell_channels++;
+ entry->users_resolved = TRUE;
+ }
return entry;
}
SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
{
+ SilcServer server = app_context;
SilcServerChannelRekey rekey = (SilcServerChannelRekey)context;
- SilcServer server = (SilcServer)rekey->context;
- silc_server_create_channel_key(server, rekey->channel, rekey->key_len);
- silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
+ 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;
- silc_task_register(server->timeout_queue, 0,
- silc_server_channel_key_rekey,
- (void *)rekey, 3600, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_server_send_channel_key(server, NULL, rekey->channel, FALSE);
}
/* Generates new channel key. This is used to create the initial channel key
but also to re-generate new key for channel. If `key_len' is provided
it is the bytes of the key length. */
-void silc_server_create_channel_key(SilcServer server,
- SilcChannelEntry channel,
- uint32 key_len)
+SilcBool silc_server_create_channel_key(SilcServer server,
+ SilcChannelEntry channel,
+ SilcUInt32 key_len)
{
int i;
- unsigned char channel_key[32], hash[32];
- uint32 len;
-
- SILC_LOG_DEBUG(("Generating channel key"));
+ unsigned char channel_key[32], hash[SILC_HASH_MAXLEN];
+ SilcUInt32 len;
if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
- return;
+ return TRUE;
}
- if (!channel->channel_key)
- if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
- return;
+ SILC_LOG_DEBUG(("Generating channel %s key", channel->channel_name));
+
+ if (!channel->send_key)
+ if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->send_key)) {
+ channel->send_key = NULL;
+ return FALSE;
+ }
+ if (!channel->receive_key)
+ if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->receive_key)) {
+ silc_cipher_free(channel->send_key);
+ channel->send_key = channel->receive_key = NULL;
+ return FALSE;
+ }
if (key_len)
len = key_len;
else if (channel->key_len)
len = channel->key_len / 8;
else
- len = silc_cipher_get_key_len(channel->channel_key) / 8;
+ len = silc_cipher_get_key_len(channel->send_key) / 8;
/* Create channel key */
for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
-
+
/* Set the key */
- silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
+ silc_cipher_set_key(channel->send_key, channel_key, len * 8, TRUE);
+ silc_cipher_set_key(channel->receive_key, channel_key, len * 8, FALSE);
/* Remove old key if exists */
if (channel->key) {
/* Save the key */
channel->key_len = len * 8;
- channel->key = silc_calloc(len, sizeof(*channel->key));
- memcpy(channel->key, channel_key, len);
+ channel->key = silc_memdup(channel_key, len);
memset(channel_key, 0, sizeof(channel_key));
/* Generate HMAC key from the channel key data and set it */
if (!channel->hmac)
- silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
- silc_hash_make(channel->hmac->hash, channel->key, len, hash);
- silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+ if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+ memset(channel->key, 0, channel->key_len / 8);
+ silc_free(channel->key);
+ silc_cipher_free(channel->send_key);
+ silc_cipher_free(channel->receive_key);
+ channel->send_key = channel->receive_key = NULL;
+ return FALSE;
+ }
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
memset(hash, 0, sizeof(hash));
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;
-
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_channel_key_rekey);
- silc_task_register(server->timeout_queue, 0,
- silc_server_channel_key_rekey,
- (void *)channel->rekey, 3600, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ if (channel->rekey->task)
+ silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+ channel->rekey->task =
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_channel_key_rekey,
+ (void *)channel->rekey,
+ server->config->channel_rekey_secs, 0);
}
+
+ return TRUE;
}
-/* Saves the channel key found in the encoded `key_payload' buffer. This
+/* Saves the channel key found in the encoded `key_payload' buffer. This
function is used when we receive Channel Key Payload and also when we're
processing JOIN command reply. Returns entry to the channel. */
SilcChannelEntry channel)
{
SilcChannelKeyPayload payload = NULL;
- SilcChannelID *id = NULL;
- unsigned char *tmp, hash[32];
- uint32 tmp_len;
+ SilcChannelID id;
+ unsigned char *tmp, hash[SILC_HASH_MAXLEN];
+ SilcUInt32 tmp_len;
char *cipher;
- SILC_LOG_DEBUG(("Start"));
-
/* Decode channel key payload */
- payload = silc_channel_key_payload_parse(key_payload);
+ payload = silc_channel_key_payload_parse(key_payload->data,
+ silc_buffer_len(key_payload));
if (!payload) {
- SILC_LOG_ERROR(("Bad channel key payload, dropped"));
+ SILC_LOG_ERROR(("Bad channel key payload received, dropped"));
channel = NULL;
goto out;
}
/* Get channel ID */
tmp = silc_channel_key_get_id(payload, &tmp_len);
- id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
- if (!id) {
+ if (!silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
channel = NULL;
goto out;
}
- channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
+ channel = silc_idlist_find_channel_by_id(server->local_list, &id, NULL);
if (!channel) {
- channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+ channel = silc_idlist_find_channel_by_id(server->global_list, &id, NULL);
if (!channel) {
- SILC_LOG_ERROR(("Received key for non-existent 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 (channel->key) {
memset(channel->key, 0, channel->key_len / 8);
silc_free(channel->key);
- silc_cipher_free(channel->channel_key);
+ silc_cipher_free(channel->send_key);
+ silc_cipher_free(channel->receive_key);
}
/* Create new cipher */
- if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
+ if (!silc_cipher_alloc(cipher, &channel->send_key)) {
+ channel->send_key = NULL;
+ channel = NULL;
+ goto out;
+ }
+ if (!silc_cipher_alloc(cipher, &channel->receive_key)) {
+ silc_cipher_free(channel->send_key);
+ channel->send_key = channel->receive_key = NULL;
channel = NULL;
goto out;
}
/* Save the key */
channel->key_len = tmp_len * 8;
- channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
- memcpy(channel->key, tmp, tmp_len);
- silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
+ channel->key = silc_memdup(tmp, tmp_len);
+ silc_cipher_set_key(channel->send_key, tmp, channel->key_len, TRUE);
+ silc_cipher_set_key(channel->receive_key, tmp, channel->key_len, FALSE);
/* Generate HMAC key from the channel key data and set it */
if (!channel->hmac)
- silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
- silc_hash_make(channel->hmac->hash, tmp, tmp_len, hash);
- silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+ if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) {
+ memset(channel->key, 0, channel->key_len / 8);
+ silc_free(channel->key);
+ silc_cipher_free(channel->send_key);
+ silc_cipher_free(channel->receive_key);
+ channel->send_key = channel->receive_key = NULL;
+ return FALSE;
+ }
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
memset(hash, 0, sizeof(hash));
memset(tmp, 0, tmp_len);
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;
-
- silc_task_unregister_by_callback(server->timeout_queue,
- silc_server_channel_key_rekey);
- silc_task_register(server->timeout_queue, 0,
- silc_server_channel_key_rekey,
- (void *)channel->rekey, 3600, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ if (channel->rekey->task)
+ silc_schedule_task_del(server->schedule, channel->rekey->task);
+
+ channel->rekey->task =
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_channel_key_rekey,
+ (void *)channel->rekey,
+ server->config->channel_rekey_secs, 0);
}
out:
- if (id)
- silc_free(id);
if (payload)
silc_channel_key_payload_free(payload);
return channel;
}
-/* Heartbeat callback. This function is set as argument for the
- silc_socket_set_heartbeat function. The library will call this function
- at the set time interval. */
-
-void silc_server_perform_heartbeat(SilcSocketConnection sock,
- void *hb_context)
-{
- SilcServerHBContext hb = (SilcServerHBContext)hb_context;
-
- SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
- sock->ip));
-
- /* Send the heartbeat */
- silc_server_send_heartbeat(hb->server, sock);
-}
-
/* Returns assembled of all servers in the given ID list. The packet's
form is dictated by the New ID payload. */
static void silc_server_announce_get_servers(SilcServer server,
+ SilcServerEntry remote,
SilcIDList id_list,
- SilcBuffer *servers)
+ SilcBuffer *servers,
+ unsigned long creation_time)
{
- SilcIDCacheList list;
+ SilcList list;
SilcIDCacheEntry id_cache;
SilcServerEntry entry;
SilcBuffer idp;
+ void *tmp;
/* Go through all clients in the list */
- if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY,
- SILC_ID_SERVER, &list)) {
- if (silc_idcache_list_first(list, &id_cache)) {
- while (id_cache) {
- entry = (SilcServerEntry)id_cache->context;
-
- idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
-
- *servers = silc_buffer_realloc(*servers,
- (*servers ?
- (*servers)->truelen + idp->len :
- idp->len));
- silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
- silc_buffer_put(*servers, idp->data, idp->len);
- silc_buffer_pull(*servers, idp->len);
- silc_buffer_free(idp);
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- }
- }
+ if (silc_idcache_get_all(id_list->servers, &list)) {
+ silc_list_start(list);
+ while ((id_cache = silc_list_get(list))) {
+ entry = (SilcServerEntry)id_cache->context;
+
+ /* Do not announce the one we've sending our announcements and
+ do not announce ourself. Also check the creation time if it's
+ provided. */
+ if ((entry == remote) || (entry == server->id_entry) ||
+ (creation_time && entry->data.created < creation_time))
+ continue;
+
+ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
- silc_idcache_list_free(list);
+ tmp = silc_buffer_realloc(*servers,
+ (*servers ?
+ silc_buffer_truelen((*servers)) +
+ silc_buffer_len(idp) :
+ silc_buffer_len(idp)));
+ if (!tmp)
+ return;
+ *servers = tmp;
+ silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
+ silc_buffer_put(*servers, idp->data, silc_buffer_len(idp));
+ silc_buffer_pull(*servers, silc_buffer_len(idp));
+ silc_buffer_free(idp);
+ }
}
}
+static SilcBuffer
+silc_server_announce_encode_notify(SilcNotifyType notify, SilcUInt32 argc, ...)
+{
+ va_list ap;
+ SilcBuffer p;
+
+ va_start(ap, argc);
+ p = silc_notify_payload_encode(notify, argc, ap);
+ va_end(ap);
+
+ return p;
+}
+
/* This function is used by router to announce existing servers to our
- primary router when we've connected to it. */
+ primary router when we've connected to it. If `creation_time' is non-zero
+ then only the servers that has been created after the `creation_time'
+ will be announced. */
-void silc_server_announce_servers(SilcServer server)
+void silc_server_announce_servers(SilcServer server, SilcBool global,
+ unsigned long creation_time,
+ SilcPacketStream remote)
{
SilcBuffer servers = NULL;
SILC_LOG_DEBUG(("Announcing servers"));
/* Get servers in local list */
- silc_server_announce_get_servers(server, server->local_list, &servers);
+ silc_server_announce_get_servers(server, silc_packet_get_context(remote),
+ server->local_list, &servers,
+ creation_time);
- /* Get servers in global list */
- silc_server_announce_get_servers(server, server->global_list, &servers);
+ if (global)
+ /* Get servers in global list */
+ silc_server_announce_get_servers(server, silc_packet_get_context(remote),
+ server->global_list, &servers,
+ creation_time);
if (servers) {
silc_buffer_push(servers, servers->data - servers->head);
- SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
+ SILC_LOG_HEXDUMP(("servers"), servers->data, silc_buffer_len(servers));
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
- servers->data, servers->len, TRUE);
+ servers->data, silc_buffer_len(servers));
silc_buffer_free(servers);
}
static void silc_server_announce_get_clients(SilcServer server,
SilcIDList id_list,
- SilcBuffer *clients)
+ SilcBuffer *clients,
+ SilcBuffer *umodes,
+ unsigned long creation_time)
{
- SilcIDCacheList list;
+ SilcList list;
SilcIDCacheEntry id_cache;
SilcClientEntry client;
SilcBuffer idp;
+ SilcBuffer tmp;
+ unsigned char mode[4];
+ void *tmp2;
/* Go through all clients in the list */
- if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY,
- SILC_ID_CLIENT, &list)) {
- if (silc_idcache_list_first(list, &id_cache)) {
- while (id_cache) {
- client = (SilcClientEntry)id_cache->context;
-
- idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
- *clients = silc_buffer_realloc(*clients,
- (*clients ?
- (*clients)->truelen + idp->len :
- idp->len));
- silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
- silc_buffer_put(*clients, idp->data, idp->len);
- silc_buffer_pull(*clients, idp->len);
- silc_buffer_free(idp);
-
- if (!silc_idcache_list_next(list, &id_cache))
- break;
- }
- }
+ if (silc_idcache_get_all(id_list->clients, &list)) {
+ silc_list_start(list);
+ while ((id_cache = silc_list_get(list))) {
+ client = (SilcClientEntry)id_cache->context;
+
+ if (creation_time && client->data.created < creation_time)
+ continue;
+ if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
+ continue;
+ if (!client->connection && !client->router)
+ continue;
- silc_idcache_list_free(list);
+ SILC_LOG_DEBUG(("Announce Client ID %s",
+ silc_id_render(client->id, SILC_ID_CLIENT)));
+
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (!idp)
+ return;
+
+ tmp2 = silc_buffer_realloc(*clients,
+ (*clients ?
+ silc_buffer_truelen((*clients)) +
+ silc_buffer_len(idp) :
+ silc_buffer_len(idp)));
+ if (!tmp2)
+ return;
+ *clients = tmp2;
+ silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
+ silc_buffer_put(*clients, idp->data, silc_buffer_len(idp));
+ silc_buffer_pull(*clients, silc_buffer_len(idp));
+
+ SILC_PUT32_MSB(client->mode, mode);
+ tmp =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+ 2, idp->data, silc_buffer_len(idp),
+ mode, 4);
+ tmp2 = silc_buffer_realloc(*umodes,
+ (*umodes ?
+ silc_buffer_truelen((*umodes)) +
+ silc_buffer_len(tmp) :
+ silc_buffer_len(tmp)));
+ if (!tmp2)
+ return;
+ *umodes = tmp2;
+ silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+ silc_buffer_put(*umodes, tmp->data, silc_buffer_len(tmp));
+ silc_buffer_pull(*umodes, silc_buffer_len(tmp));
+ silc_buffer_free(tmp);
+
+ silc_buffer_free(idp);
+ }
}
}
/* This function is used to announce our existing clients to our router
- when we've connected to it. */
+ when we've connected to it. If `creation_time' is non-zero then only
+ the clients that has been created after the `creation_time' will be
+ announced. */
-void silc_server_announce_clients(SilcServer server)
+void silc_server_announce_clients(SilcServer server,
+ unsigned long creation_time,
+ SilcPacketStream remote)
{
SilcBuffer clients = NULL;
+ SilcBuffer umodes = NULL;
SILC_LOG_DEBUG(("Announcing clients"));
/* Get clients in local list */
silc_server_announce_get_clients(server, server->local_list,
- &clients);
+ &clients, &umodes, creation_time);
/* As router we announce our global list as well */
if (server->server_type == SILC_ROUTER)
silc_server_announce_get_clients(server, server->global_list,
- &clients);
+ &clients, &umodes, creation_time);
if (clients) {
silc_buffer_push(clients, clients->data - clients->head);
- SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
+ SILC_LOG_HEXDUMP(("clients"), clients->data, silc_buffer_len(clients));
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
- clients->data, clients->len, TRUE);
+ clients->data, silc_buffer_len(clients));
silc_buffer_free(clients);
}
+
+ if (umodes) {
+ silc_buffer_push(umodes, umodes->data - umodes->head);
+ SILC_LOG_HEXDUMP(("umodes"), umodes->data, silc_buffer_len(umodes));
+
+ /* Send the packet */
+ silc_server_packet_send(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ umodes->data, silc_buffer_len(umodes));
+
+ silc_buffer_free(umodes);
+ }
}
-static SilcBuffer
-silc_server_announce_encode_join(uint32 argc, ...)
+/* Returns channel's topic for announcing it */
+
+void silc_server_announce_get_channel_topic(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *topic)
{
- va_list ap;
+ SilcBuffer chidp;
+
+ if (channel->topic) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+ chidp->data,
+ silc_buffer_len(chidp),
+ channel->topic,
+ strlen(channel->topic));
+ silc_buffer_free(chidp);
+ }
+}
- va_start(ap, argc);
- return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
+/* Returns channel's invite and ban lists */
+
+void silc_server_announce_get_inviteban(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *invite,
+ SilcBuffer *ban)
+{
+ SilcBuffer list, idp, idp2, tmp2;
+ SilcUInt32 type;
+ void *ptype;
+ SilcHashTableList htl;
+ const unsigned char a[1] = { 0x03 };
+
+ idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+
+ /* Encode invite list */
+ if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+ list = silc_buffer_alloc_size(2);
+ type = silc_hash_table_count(channel->invite_list);
+ SILC_PUT16_MSB(type, list->data);
+ silc_hash_table_list(channel->invite_list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2))
+ list = silc_argument_payload_encode_one(list, tmp2->data,
+ silc_buffer_len(tmp2),
+ SILC_PTR_TO_32(ptype));
+ silc_hash_table_list_reset(&htl);
+
+ idp2 = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+ *invite =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_INVITE, 5,
+ idp->data, silc_buffer_len(idp),
+ channel->channel_name,
+ strlen(channel->channel_name),
+ idp2->data, silc_buffer_len(idp2),
+ a, 1,
+ list->data, silc_buffer_len(list));
+ silc_buffer_free(idp2);
+ silc_buffer_free(list);
+ }
+
+ /* Encode ban list */
+ if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+ list = silc_buffer_alloc_size(2);
+ type = silc_hash_table_count(channel->ban_list);
+ SILC_PUT16_MSB(type, list->data);
+ silc_hash_table_list(channel->ban_list, &htl);
+ while (silc_hash_table_get(&htl, (void *)&ptype, (void *)&tmp2))
+ list = silc_argument_payload_encode_one(list, tmp2->data,
+ silc_buffer_len(tmp2),
+ SILC_PTR_TO_32(ptype));
+ silc_hash_table_list_reset(&htl);
+
+ *ban =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_BAN, 3,
+ idp->data, silc_buffer_len(idp),
+ a, 1,
+ list->data, silc_buffer_len(list));
+ silc_buffer_free(list);
+ }
+
+ silc_buffer_free(idp);
}
/* Returns assembled packets for channel users of the `channel'. */
void silc_server_announce_get_channel_users(SilcServer server,
SilcChannelEntry channel,
- SilcBuffer *channel_users)
+ SilcBuffer *channel_modes,
+ SilcBuffer *channel_users,
+ SilcBuffer *channel_users_modes)
{
SilcChannelClientEntry chl;
- SilcBuffer chidp, clidp;
- SilcBuffer tmp;
+ SilcHashTableList htl;
+ SilcBuffer chidp, clidp, csidp;
+ SilcBuffer tmp, fkey = NULL, chpklist;
int len;
+ unsigned char mode[4], ulimit[4];
+ char *hmac;
+ void *tmp2;
SILC_LOG_DEBUG(("Start"));
- /* Now find all users on the channel */
chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ csidp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+ chpklist = silc_server_get_channel_pk_list(server, channel, TRUE, FALSE);
+
+ /* CMODE notify */
+ SILC_PUT32_MSB(channel->mode, mode);
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ SILC_PUT32_MSB(channel->user_limit, ulimit);
+ hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
+ if (channel->founder_key)
+ fkey = silc_public_key_payload_encode(channel->founder_key);
+ tmp =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
+ 8, csidp->data,
+ silc_buffer_len(csidp),
+ mode, sizeof(mode),
+ NULL, 0,
+ hmac, hmac ? strlen(hmac) : 0,
+ channel->passphrase,
+ channel->passphrase ?
+ strlen(channel->passphrase) : 0,
+ fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0,
+ chpklist ? chpklist->data : NULL,
+ chpklist ?
+ silc_buffer_len(chpklist) : 0,
+ (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL),
+ (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 0));
+ len = silc_buffer_len(tmp);
+ tmp2 =
+ silc_buffer_realloc(*channel_modes,
+ (*channel_modes ?
+ silc_buffer_truelen((*channel_modes)) + len : len));
+ if (!tmp2)
+ return;
+ *channel_modes = tmp2;
+ silc_buffer_pull_tail(*channel_modes,
+ ((*channel_modes)->end -
+ (*channel_modes)->data));
+ silc_buffer_put(*channel_modes, tmp->data, silc_buffer_len(tmp));
+ silc_buffer_pull(*channel_modes, len);
+ silc_buffer_free(tmp);
+ silc_buffer_free(fkey);
+ fkey = NULL;
+
+ /* Now find all users on the channel */
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
- tmp = silc_server_announce_encode_join(2, clidp->data, clidp->len,
- chidp->data, chidp->len);
- len = tmp->len;
- *channel_users =
- silc_buffer_realloc(*channel_users,
- (*channel_users ?
- (*channel_users)->truelen + len : len));
- silc_buffer_pull_tail(*channel_users,
- ((*channel_users)->end -
+
+ SILC_LOG_DEBUG(("JOIN Client %s", silc_id_render(chl->client->id,
+ SILC_ID_CLIENT)));
+
+ /* JOIN Notify */
+ tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_JOIN, 2,
+ clidp->data,
+ silc_buffer_len(clidp),
+ chidp->data,
+ silc_buffer_len(chidp));
+ len = silc_buffer_len(tmp);
+ tmp2 =
+ silc_buffer_realloc(*channel_users,
+ (*channel_users ?
+ silc_buffer_truelen((*channel_users)) + len : len));
+ if (!tmp2)
+ return;
+ *channel_users = tmp2;
+ silc_buffer_pull_tail(*channel_users,
+ ((*channel_users)->end -
(*channel_users)->data));
-
- silc_buffer_put(*channel_users, tmp->data, tmp->len);
+
+ silc_buffer_put(*channel_users, tmp->data, silc_buffer_len(tmp));
silc_buffer_pull(*channel_users, len);
- silc_buffer_free(clidp);
silc_buffer_free(tmp);
+
+ /* 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_public_key_payload_encode(channel->founder_key);
+ tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
+ 4, csidp->data,
+ silc_buffer_len(csidp),
+ mode, sizeof(mode),
+ clidp->data,
+ silc_buffer_len(clidp),
+ fkey ? fkey->data : NULL,
+ fkey ? silc_buffer_len(fkey) : 0);
+ len = silc_buffer_len(tmp);
+ tmp2 =
+ silc_buffer_realloc(*channel_users_modes,
+ (*channel_users_modes ?
+ silc_buffer_truelen((*channel_users_modes)) +
+ len : len));
+ if (!tmp2)
+ return;
+ *channel_users_modes = tmp2;
+ silc_buffer_pull_tail(*channel_users_modes,
+ ((*channel_users_modes)->end -
+ (*channel_users_modes)->data));
+
+ silc_buffer_put(*channel_users_modes, tmp->data, silc_buffer_len(tmp));
+ silc_buffer_pull(*channel_users_modes, len);
+ silc_buffer_free(tmp);
+ silc_buffer_free(fkey);
+ fkey = NULL;
+ silc_buffer_free(clidp);
}
+ silc_hash_table_list_reset(&htl);
silc_buffer_free(chidp);
+ silc_buffer_free(csidp);
}
/* Returns assembled packets for all channels and users on those channels
void silc_server_announce_get_channels(SilcServer server,
SilcIDList id_list,
SilcBuffer *channels,
- SilcBuffer *channel_users)
+ SilcBuffer **channel_modes,
+ SilcBuffer *channel_users,
+ SilcBuffer **channel_users_modes,
+ SilcUInt32 *channel_users_modes_c,
+ SilcBuffer **channel_topics,
+ SilcBuffer **channel_invites,
+ SilcBuffer **channel_bans,
+ SilcChannelID ***channel_ids,
+ unsigned long creation_time)
{
- SilcIDCacheList list;
+ SilcList list;
SilcIDCacheEntry id_cache;
SilcChannelEntry channel;
- unsigned char *cid;
- uint16 name_len;
+ unsigned char cid[32];
+ SilcUInt32 id_len;
+ SilcUInt16 name_len;
int len;
+ int i = *channel_users_modes_c;
+ void *tmp;
+ SilcBool announce;
SILC_LOG_DEBUG(("Start"));
/* Go through all channels in the list */
- if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY,
- SILC_ID_CHANNEL, &list)) {
- if (silc_idcache_list_first(list, &id_cache)) {
- while (id_cache) {
- channel = (SilcChannelEntry)id_cache->context;
-
- cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
- name_len = strlen(channel->channel_name);
-
- len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
- *channels =
- silc_buffer_realloc(*channels,
- (*channels ? (*channels)->truelen + len : len));
- silc_buffer_pull_tail(*channels,
+ if (silc_idcache_get_all(id_list->channels, &list)) {
+ silc_list_start(list);
+ while ((id_cache = silc_list_get(list))) {
+ channel = (SilcChannelEntry)id_cache->context;
+
+ if (creation_time && channel->created < creation_time)
+ announce = FALSE;
+ else
+ announce = TRUE;
+
+ SILC_LOG_DEBUG(("Announce Channel ID %s",
+ silc_id_render(channel->id, SILC_ID_CHANNEL)));
+
+ silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
+ name_len = strlen(channel->channel_name);
+
+ if (announce) {
+ len = 4 + name_len + id_len + 4;
+ tmp =
+ silc_buffer_realloc(*channels,
+ (*channels ?
+ silc_buffer_truelen((*channels)) +
+ len : len));
+ if (!tmp)
+ break;
+ *channels = tmp;
+
+ silc_buffer_pull_tail(*channels,
((*channels)->end - (*channels)->data));
silc_buffer_format(*channels,
SILC_STR_UI_SHORT(name_len),
- SILC_STR_UI_XNSTRING(channel->channel_name,
+ SILC_STR_UI_XNSTRING(channel->channel_name,
name_len),
- SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
- SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_UI_XNSTRING(cid, id_len),
SILC_STR_UI_INT(channel->mode),
SILC_STR_END);
silc_buffer_pull(*channels, len);
+ }
+
+ if (creation_time && channel->updated < creation_time)
+ announce = FALSE;
+ else
+ announce = TRUE;
+ if (announce) {
+ /* Channel user modes */
+ tmp = silc_realloc(*channel_users_modes,
+ sizeof(**channel_users_modes) * (i + 1));
+ if (!tmp)
+ break;
+ *channel_users_modes = tmp;
+ (*channel_users_modes)[i] = NULL;
+ tmp = silc_realloc(*channel_modes,
+ sizeof(**channel_modes) * (i + 1));
+ if (!tmp)
+ break;
+ *channel_modes = tmp;
+ (*channel_modes)[i] = NULL;
+ tmp = silc_realloc(*channel_ids,
+ sizeof(**channel_ids) * (i + 1));
+ if (!tmp)
+ break;
+ *channel_ids = tmp;
+ (*channel_ids)[i] = NULL;
silc_server_announce_get_channel_users(server, channel,
- channel_users);
+ &(*channel_modes)[i],
+ channel_users,
+ &(*channel_users_modes)[i]);
+ (*channel_ids)[i] = channel->id;
+
+ /* Channel's topic */
+ tmp = silc_realloc(*channel_topics,
+ sizeof(**channel_topics) * (i + 1));
+ if (!tmp)
+ break;
+ *channel_topics = tmp;
+ (*channel_topics)[i] = NULL;
+ silc_server_announce_get_channel_topic(server, channel,
+ &(*channel_topics)[i]);
+
+ /* Channel's invite and ban list */
+ tmp = silc_realloc(*channel_invites,
+ sizeof(**channel_invites) * (i + 1));
+ if (!tmp)
+ break;
+ *channel_invites = tmp;
+ (*channel_invites)[i] = NULL;
+ tmp = silc_realloc(*channel_bans,
+ sizeof(**channel_bans) * (i + 1));
+ if (!tmp)
+ break;
+ *channel_bans = tmp;
+ (*channel_bans)[i] = NULL;
+ silc_server_announce_get_inviteban(server, channel,
+ &(*channel_invites)[i],
+ &(*channel_bans)[i]);
- silc_free(cid);
+ (*channel_users_modes_c)++;
- if (!silc_idcache_list_next(list, &id_cache))
- break;
+ i++;
}
}
-
- silc_idcache_list_free(list);
}
}
/* This function is used to announce our existing channels to our router
when we've connected to it. This also announces the users on the
- channels to the router. */
-
-void silc_server_announce_channels(SilcServer server)
+ channels to the router. If the `creation_time' is non-zero only the
+ channels that was created after the `creation_time' are announced.
+ Note that the channel users are still announced even if the `creation_time'
+ was provided. */
+
+void silc_server_announce_channels(SilcServer server,
+ unsigned long creation_time,
+ SilcPacketStream remote)
{
- SilcBuffer channels = NULL, channel_users = NULL;
+ SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
+ SilcBuffer *channel_users_modes = NULL;
+ SilcBuffer *channel_topics = NULL;
+ SilcBuffer *channel_invites = NULL;
+ SilcBuffer *channel_bans = NULL;
+ SilcUInt32 channel_users_modes_c = 0;
+ SilcChannelID **channel_ids = NULL;
SILC_LOG_DEBUG(("Announcing channels and channel users"));
/* Get channels and channel users in local list */
silc_server_announce_get_channels(server, server->local_list,
- &channels, &channel_users);
+ &channels, &channel_modes,
+ &channel_users,
+ &channel_users_modes,
+ &channel_users_modes_c,
+ &channel_topics,
+ &channel_invites,
+ &channel_bans,
+ &channel_ids, creation_time);
/* Get channels and channel users in global list */
- silc_server_announce_get_channels(server, server->global_list,
- &channels, &channel_users);
+ if (server->server_type != SILC_SERVER)
+ silc_server_announce_get_channels(server, server->global_list,
+ &channels, &channel_modes,
+ &channel_users,
+ &channel_users_modes,
+ &channel_users_modes_c,
+ &channel_topics,
+ &channel_invites,
+ &channel_bans,
+ &channel_ids, creation_time);
if (channels) {
silc_buffer_push(channels, channels->data - channels->head);
- SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
+ SILC_LOG_HEXDUMP(("channels"), channels->data, silc_buffer_len(channels));
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
- channels->data, channels->len,
- FALSE);
+ channels->data, silc_buffer_len(channels));
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);
+ SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
+ silc_buffer_len(channel_users));
/* Send the packet */
- silc_server_packet_send(server, server->router->connection,
+ silc_server_packet_send(server, remote,
SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
- channel_users->data, channel_users->len,
- FALSE);
+ channel_users->data, silc_buffer_len(channel_users));
silc_buffer_free(channel_users);
}
+
+ if (channel_modes) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_modes[i])
+ continue;
+ silc_buffer_push(channel_modes[i],
+ channel_modes[i]->data -
+ channel_modes[i]->head);
+ SILC_LOG_HEXDUMP(("channel modes"), channel_modes[i]->data,
+ silc_buffer_len(channel_modes[i]));
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_modes[i]->data,
+ silc_buffer_len(channel_modes[i]));
+ silc_buffer_free(channel_modes[i]);
+ }
+ silc_free(channel_modes);
+ }
+
+ if (channel_users_modes) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_users_modes[i])
+ continue;
+ silc_buffer_push(channel_users_modes[i],
+ channel_users_modes[i]->data -
+ channel_users_modes[i]->head);
+ SILC_LOG_HEXDUMP(("channel users modes"), channel_users_modes[i]->data,
+ silc_buffer_len(channel_users_modes[i]));
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_users_modes[i]->data,
+ silc_buffer_len(channel_users_modes[i]));
+ silc_buffer_free(channel_users_modes[i]);
+ }
+ silc_free(channel_users_modes);
+ }
+
+ if (channel_topics) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_topics[i])
+ continue;
+
+ silc_buffer_push(channel_topics[i],
+ channel_topics[i]->data -
+ channel_topics[i]->head);
+ SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data,
+ silc_buffer_len(channel_topics[i]));
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_topics[i]->data,
+ silc_buffer_len(channel_topics[i]));
+ silc_buffer_free(channel_topics[i]);
+ }
+ silc_free(channel_topics);
+ }
+
+ if (channel_invites) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_invites[i])
+ continue;
+
+ silc_buffer_push(channel_invites[i],
+ channel_invites[i]->data -
+ channel_invites[i]->head);
+ SILC_LOG_HEXDUMP(("channel invite list"), channel_invites[i]->data,
+ silc_buffer_len(channel_invites[i]));
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_invites[i]->data,
+ silc_buffer_len(channel_invites[i]));
+ silc_buffer_free(channel_invites[i]);
+ }
+ silc_free(channel_invites);
+ }
+
+ if (channel_bans) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_bans[i])
+ continue;
+
+ silc_buffer_push(channel_bans[i],
+ channel_bans[i]->data -
+ channel_bans[i]->head);
+ SILC_LOG_HEXDUMP(("channel ban list"), channel_bans[i]->data,
+ silc_buffer_len(channel_bans[i]));
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_bans[i]->data,
+ silc_buffer_len(channel_bans[i]));
+ silc_buffer_free(channel_bans[i]);
+ }
+ silc_free(channel_bans);
+ }
+
+ silc_free(channel_ids);
}
-/* Failure timeout callback. If this is called then we will immediately
- process the received failure. We always process the failure with timeout
- since we do not want to blindly trust to received failure packets.
- This won't be called (the timeout is cancelled) if the failure was
- bogus (it is bogus if remote does not close the connection after sending
- the failure). */
+/* Announces WATCH list. */
-SILC_TASK_CALLBACK(silc_server_failure_callback)
+void silc_server_announce_watches(SilcServer server,
+ SilcPacketStream remote)
{
- SilcServerFailureContext f = (SilcServerFailureContext)context;
+ SilcHashTableList htl;
+ SilcBuffer buffer, idp, args, pkp;
+ SilcClientEntry client;
+ void *key;
- if (f->sock->protocol) {
- f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- f->sock->protocol->execute(f->server->timeout_queue, 0,
- f->sock->protocol, f->sock->sock, 0, 0);
- }
+ SILC_LOG_DEBUG(("Announcing watch list"));
+
+ /* XXX because way we save the nicks (hash) we cannot announce them. */
- silc_free(f);
+ /* XXX we should send all public keys in one command if client is
+ watching more than one key */
+ silc_hash_table_list(server->watcher_list_pk, &htl);
+ while (silc_hash_table_get(&htl, &key, (void *)&client)) {
+ if (!client || !client->id)
+ continue;
+
+ server->stat.commands_sent++;
+
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ args = silc_buffer_alloc_size(2);
+ silc_buffer_format(args,
+ SILC_STR_UI_SHORT(1),
+ SILC_STR_END);
+ pkp = silc_public_key_payload_encode(key);
+ args = silc_argument_payload_encode_one(args, pkp->data,
+ silc_buffer_len(pkp), 0x00);
+ buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH,
+ ++server->cmd_ident, 2,
+ 1, idp->data, silc_buffer_len(idp),
+ 4, args->data,
+ silc_buffer_len(args));
+
+ /* Send command */
+ silc_server_packet_send(server, remote, SILC_PACKET_COMMAND, 0,
+ buffer->data, silc_buffer_len(buffer));
+
+ silc_buffer_free(pkp);
+ silc_buffer_free(args);
+ silc_buffer_free(idp);
+ silc_buffer_free(buffer);
+ }
+ silc_hash_table_list_reset(&htl);
}
/* Assembles user list and users mode list from the `channel'. */
-void silc_server_get_users_on_channel(SilcServer server,
- SilcChannelEntry channel,
- SilcBuffer *user_list,
- SilcBuffer *mode_list,
- uint32 *user_count)
+SilcBool silc_server_get_users_on_channel(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *user_list,
+ SilcBuffer *mode_list,
+ SilcUInt32 *user_count)
{
SilcChannelClientEntry chl;
+ SilcHashTableList htl;
SilcBuffer client_id_list;
SilcBuffer client_mode_list;
SilcBuffer idp;
- uint32 list_count = 0;
-
- client_id_list = silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) *
- silc_list_count(channel->user_list));
- client_mode_list = silc_buffer_alloc(4 *
- silc_list_count(channel->user_list));
- silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
- silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
- silc_list_start(channel->user_list);
- while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ SilcUInt32 list_count = 0, len = 0;
+
+ if (!silc_hash_table_count(channel->user_list))
+ return FALSE;
+
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+ len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
+ silc_hash_table_list_reset(&htl);
+
+ client_id_list = silc_buffer_alloc(len);
+ client_mode_list =
+ silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
+ silc_buffer_pull_tail(client_id_list, silc_buffer_truelen(client_id_list));
+ silc_buffer_pull_tail(client_mode_list,
+ silc_buffer_truelen(client_mode_list));
+
+ silc_hash_table_list(channel->user_list, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
/* Client ID */
idp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
- silc_buffer_put(client_id_list, idp->data, idp->len);
- silc_buffer_pull(client_id_list, idp->len);
+ silc_buffer_put(client_id_list, idp->data, silc_buffer_len(idp));
+ silc_buffer_pull(client_id_list, silc_buffer_len(idp));
silc_buffer_free(idp);
/* Client's mode on channel */
list_count++;
}
- silc_buffer_push(client_id_list,
+ silc_hash_table_list_reset(&htl);
+ silc_buffer_push(client_id_list,
client_id_list->data - client_id_list->head);
- silc_buffer_push(client_mode_list,
+ silc_buffer_push(client_mode_list,
client_mode_list->data - client_mode_list->head);
*user_list = client_id_list;
*mode_list = client_mode_list;
*user_count = list_count;
+ return TRUE;
}
/* Saves users and their modes to the `channel'. */
void silc_server_save_users_on_channel(SilcServer server,
- SilcSocketConnection sock,
+ SilcPacketStream sock,
SilcChannelEntry channel,
SilcClientID *noadd,
SilcBuffer user_list,
SilcBuffer mode_list,
- uint32 user_count)
+ SilcUInt32 user_count)
{
int i;
+ SilcUInt16 idp_len;
+ SilcUInt32 mode;
+ SilcID id;
+ SilcClientEntry client;
+ SilcIDCacheEntry cache;
+ SilcChannelClientEntry chl;
- /* Cache the received Client ID's and modes. This cache expires
- whenever server sends notify message to channel. It means two things;
- some user has joined or leaved the channel. XXX TODO! */
- for (i = 0; i < user_count; i++) {
- uint16 idp_len;
- uint32 mode;
- SilcClientID *client_id;
- SilcClientEntry client;
+ SILC_LOG_DEBUG(("Saving %d users on %s channel", user_count,
+ channel->channel_name));
+ for (i = 0; i < user_count; i++) {
/* Client ID */
SILC_GET16_MSB(idp_len, user_list->data + 2);
idp_len += 4;
- client_id = silc_id_payload_parse_id(user_list->data, idp_len);
- silc_buffer_pull(user_list, idp_len);
- if (!client_id)
+ if (!silc_id_payload_parse_id(user_list->data, idp_len, &id))
continue;
+ silc_buffer_pull(user_list, idp_len);
/* Mode */
SILC_GET32_MSB(mode, mode_list->data);
silc_buffer_pull(mode_list, 4);
- if (noadd && !SILC_ID_CLIENT_COMPARE(client_id, noadd)) {
- silc_free(client_id);
+ if (noadd && SILC_ID_CLIENT_COMPARE(&id.u.client_id, noadd))
continue;
- }
-
+
+ cache = NULL;
+
/* Check if we have this client cached already. */
- client = silc_idlist_find_client_by_id(server->local_list, client_id,
- NULL);
+ client = silc_idlist_find_client_by_id(server->local_list,
+ &id.u.client_id,
+ server->server_type, &cache);
if (!client)
- client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
+ client = silc_idlist_find_client_by_id(server->global_list,
+ &id.u.client_id,
+ server->server_type, &cache);
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) {
- silc_free(client_id);
+ if (server->server_type != SILC_SERVER)
continue;
- }
/* We don't have that client anywhere, add it. The client is added
- to global list since server didn't have it in the lists so it must be
+ to global list since server didn't have it in the lists so it must be
global. */
- client = silc_idlist_add_client(server->global_list, NULL, 0, NULL,
- NULL,
- silc_id_dup(client_id, SILC_ID_CLIENT),
- sock->user_data, NULL);
+ client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
+ silc_id_dup(&id.u.client_id,
+ SILC_ID_CLIENT),
+ silc_packet_get_context(sock),
+ NULL);
if (!client) {
- silc_free(client_id);
+ SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
continue;
}
- client->data.registered = TRUE;
+ client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
}
- 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)) {
+ 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_list_add(channel->user_list, chl);
- silc_list_add(client->channels, chl);
+ 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,
+ SilcPacketStream 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 ||
+ !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
+ goto out;
+
+ ch = silc_channel_payload_parse_list(channels->data,
+ silc_buffer_len(channels));
+ 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. */
+ if (!silc_channel_get_id_parse(entry, &channel_id))
+ continue;
+ 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) {
+ 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,
+ silc_id_dup(&channel_id,
+ SILC_ID_CHANNEL),
+ server->router, NULL, NULL, 0);
+ if (!channel) {
+ i++;
+ continue;
+ }
+ }
+
+ 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_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 `id' client ID. The connection
+/* 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,
- uint32 id_len,
- SilcClientID *client_id,
- SilcIDListData *idata)
+SilcPacketStream
+silc_server_get_client_route(SilcServer server,
+ unsigned char *id_data,
+ SilcUInt32 id_len,
+ SilcClientID *client_id,
+ SilcIDListData *idata,
+ SilcClientEntry *client_entry)
{
- SilcClientID *id;
+ SilcClientID *id, clid;
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);
- if (!id) {
- SILC_LOG_ERROR(("Could not decode destination Client ID, dropped"));
+ if (!silc_id_str2id(id_data, id_len, SILC_ID_CLIENT, &clid, sizeof(clid)))
return NULL;
- }
+ id = silc_id_dup(&clid, SILC_ID_CLIENT);
} else {
id = silc_id_dup(client_id, SILC_ID_CLIENT);
}
/* If the destination belongs to our server we don't have to route
the packet anywhere but to send it to the local destination. */
- client = silc_idlist_find_client_by_id(server->local_list, id, NULL);
+ client = silc_idlist_find_client_by_id(server->local_list, id, TRUE, NULL);
if (client) {
silc_free(id);
- if (client && client->data.registered == FALSE)
- return NULL;
-
/* If we are router and the client has router then the client is in
our cell but not directly connected to us. */
if (server->server_type == SILC_ROUTER && client->router) {
- /* We are of course in this case the client's router thus the real
- "router" of the client is the server who owns the client. Thus
- we will send the packet to that server. */
- *idata = (SilcIDListData)client->router;
+ /* We are of course in this case the client's router thus the route
+ to the client is the server who owns the client. So, we will send
+ the packet to that server. */
+ if (idata)
+ *idata = (SilcIDListData)client->router;
return client->router->connection;
}
/* Seems that client really is directly connected to us */
- *idata = (SilcIDListData)client;
+ if (idata)
+ *idata = (SilcIDListData)client;
+ if (client_entry)
+ *client_entry = client;
return client->connection;
}
/* Destination belongs to someone not in this server. If we are normal
server our action is to send the packet to our router. */
- if (server->server_type == SILC_SERVER && !server->standalone) {
+ if (server->server_type != SILC_ROUTER && !server->standalone) {
silc_free(id);
- *idata = (SilcIDListData)server->router;
- return server->router->connection;
+ if (idata)
+ *idata = (SilcIDListData)server->router;
+ return SILC_PRIMARY_ROUTE(server);
}
- /* We are router and we will perform route lookup for the destination
+ /* We are router and we will perform route lookup for the destination
and send the packet to fastest route. */
if (server->server_type == SILC_ROUTER && !server->standalone) {
/* Check first that the ID is valid */
- client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ client = silc_idlist_find_client_by_id(server->global_list, id,
+ TRUE, NULL);
if (client) {
- SilcSocketConnection dst_sock;
+ SilcPacketStream dst_sock;
dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
silc_free(id);
- *idata = (SilcIDListData)dst_sock->user_data;
+ if (idata && dst_sock)
+ *idata = silc_packet_get_context(dst_sock);
return dst_sock;
}
}
Secret channels are not put to the list. */
SilcBuffer silc_server_get_client_channel_list(SilcServer server,
- SilcClientEntry client)
+ SilcClientEntry client,
+ SilcBool get_private,
+ SilcBool get_secret,
+ SilcBuffer *user_mode_list)
{
SilcBuffer buffer = NULL;
SilcChannelEntry channel;
SilcChannelClientEntry chl;
- unsigned char *cid;
- uint16 name_len;
+ SilcHashTableList htl;
+ unsigned char cid[32];
+ SilcUInt32 id_len;
+ SilcUInt16 name_len;
int len;
- silc_list_start(client->channels);
- while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+ 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)
+ 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);
+ silc_id_id2str(channel->id, SILC_ID_CHANNEL, cid, sizeof(cid), &id_len);
name_len = strlen(channel->channel_name);
-
- len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
- buffer = silc_buffer_realloc(buffer,
- (buffer ? (buffer)->truelen + len : len));
- silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+
+ len = 4 + name_len + id_len + 4;
+ buffer = silc_buffer_realloc(buffer,
+ (buffer ?
+ silc_buffer_truelen(buffer) + len : len));
+ silc_buffer_pull_tail(buffer, (buffer->end - buffer->data));
silc_buffer_format(buffer,
SILC_STR_UI_SHORT(name_len),
- SILC_STR_UI_XNSTRING(channel->channel_name,
- name_len),
- SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
- SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
- SILC_STR_UI_INT(chl->mode), /* Client's mode */
+ SILC_STR_DATA(channel->channel_name, name_len),
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_DATA(cid, id_len),
+ 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 ?
+ silc_buffer_truelen((*user_mode_list)) + 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;
}
-/* Finds client entry by Client ID and if it is not found then resolves
- it using WHOIS command. */
+/* Task callback used to retrieve network statistical information from
+ router server once in a while. */
-SilcClientEntry silc_server_get_client_resolve(SilcServer server,
- SilcClientID *client_id)
+SILC_TASK_CALLBACK(silc_server_get_stats)
{
- SilcClientEntry client;
-
- client = silc_idlist_find_client_by_id(server->local_list, client_id, NULL);
- if (!client) {
- client = silc_idlist_find_client_by_id(server->global_list,
- client_id, NULL);
- if (!client && server->server_type == SILC_ROUTER)
- return NULL;
- }
-
- if (!client && server->standalone)
- return NULL;
-
- if (!client || !client->nickname || !client->username) {
- SilcBuffer buffer, idp;
-
- idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
- buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
- ++server->cmd_ident, 1,
- 3, 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);
+ SilcServer server = (SilcServer)context;
+ SilcBuffer idp, packet;
+
+ if (!server->standalone) {
+ SILC_LOG_DEBUG(("Retrieving stats from router"));
+ server->stat.commands_sent++;
+ idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+ if (idp) {
+ packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+ ++server->cmd_ident, 1,
+ 1, idp->data,
+ silc_buffer_len(idp));
+ silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server),
+ SILC_PACKET_COMMAND, 0, packet->data,
+ silc_buffer_len(packet));
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+ }
}
- return client;
+ silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
+ server, 120, 0);
}