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
if (server->pending_commands)
silc_dlist_uninit(server->pending_commands);
- silc_math_primegen_uninit(); /* XXX */
silc_free(server);
}
}
int *sock = NULL, sock_count = 0, i;
SilcServerID *id;
SilcServerEntry id_entry;
+ SilcIDListPurge purge;
SILC_LOG_DEBUG(("Initializing server"));
assert(server);
/* Set log files where log message should be saved. */
server->config->server = server;
- silc_config_server_setlogfiles(server->config);
+ silc_server_config_setlogfiles(server->config);
/* Register all configured ciphers, PKCS and hash functions. */
- silc_config_server_register_ciphers(server->config);
- silc_config_server_register_pkcs(server->config);
- silc_config_server_register_hashfuncs(server->config);
+ 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);
/* Initialize random number generator for the server. */
server->rng = silc_rng_alloc();
silc_rng_init(server->rng);
- silc_math_primegen_init(); /* XXX */
+ silc_rng_global_init(server->rng);
/* Initialize hash functions for server to use */
silc_hash_alloc("md5", &server->md5hash);
}
/* Initialize ID caches */
- server->local_list->clients = silc_idcache_alloc(0);
- server->local_list->servers = silc_idcache_alloc(0);
- server->local_list->channels = silc_idcache_alloc(0);
+ 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);
/* 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);
- server->global_list->servers = silc_idcache_alloc(0);
- server->global_list->channels = silc_idcache_alloc(0);
+ 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
is sent as argument for fast referencing in the future. */
silc_socket_alloc(sock[i], SILC_SOCKET_TYPE_SERVER, id_entry,
&newsocket);
- if (!newsocket)
- goto err0;
server->sockets[sock[i]] = newsocket;
if (server->config->servers)
server->server_type = SILC_ROUTER;
+ /* Register the ID Cache purge task. This periodically purges the ID cache
+ and removes the expired cache entries. */
+
+ /* Clients local list */
+ purge = silc_calloc(1, sizeof(*purge));
+ purge->cache = server->local_list->clients;
+ purge->timeout_queue = server->timeout_queue;
+ silc_task_register(purge->timeout_queue, 0,
+ silc_idlist_purge,
+ (void *)purge, 600, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+ /* Clients global list */
+ purge = silc_calloc(1, sizeof(*purge));
+ purge->cache = server->global_list->clients;
+ purge->timeout_queue = server->timeout_queue;
+ silc_task_register(purge->timeout_queue, 0,
+ silc_idlist_purge,
+ (void *)purge, 300, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
SILC_LOG_DEBUG(("Server initialized"));
/* We are done here, return succesfully */
/* Allocate connection object for hold connection specific stuff. */
sconn = silc_calloc(1, sizeof(*sconn));
sconn->server = server;
- sconn->remote_host = server->config->routers->host;
+ sconn->remote_host = strdup(server->config->routers->host);
sconn->remote_port = server->config->routers->port;
silc_task_register(server->timeout_queue, fd,
/* If we are a SILC router we need to establish all of our primary
routes. */
if (server->server_type == SILC_ROUTER) {
- SilcConfigServerSectionServerConnection *ptr;
+ SilcServerConfigSectionServerConnection *ptr;
SILC_LOG_DEBUG(("We are router"));
/* Allocate connection object for hold connection specific stuff. */
sconn = silc_calloc(1, sizeof(*sconn));
sconn->server = server;
- sconn->remote_host = ptr->host;
+ sconn->remote_host = strdup(ptr->host);
sconn->remote_port = ptr->port;
silc_task_register(server->timeout_queue, fd,
SilcServerConnection sconn = (SilcServerConnection)ctx->context;
SilcSocketConnection sock = NULL;
SilcServerConnAuthInternalContext *proto_ctx;
+ SilcServerConfigSectionServerConnection *conn = NULL;
SILC_LOG_DEBUG(("Start"));
if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
/* 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)
if (ctx->dest_id)
silc_free(ctx->dest_id);
silc_free(ctx);
- sock->protocol = NULL;
+ 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->dest_id_type = ctx->dest_id_type;
proto_ctx->dest_id = ctx->dest_id;
- /* Resolve the authentication method used in this connection */
- proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
- if (server->config->routers) {
- SilcConfigServerSectionServerConnection *conn = NULL;
-
- /* Check if we find a match from user configured connections */
- conn = silc_config_server_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);
- }
- } else {
- /* No match found. */
- /* XXX */
+ /* 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);
}
} else {
- /* XXX */
+ SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
+ sock->hostname, sock->ip, sock->port));
+ 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;
}
/* Free old protocol as it is finished now */
out:
/* Free the temporary connection data context */
- if (sconn)
+ if (sconn) {
+ silc_free(sconn->remote_host);
silc_free(sconn);
+ }
/* Free the protocol object */
silc_protocol_free(protocol);
/* Check max connections */
if (sock > SILC_SERVER_MAX_CONNECTIONS) {
- if (server->config->redirect) {
- /* XXX Redirecting connection to somewhere else now?? */
- /*silc_server_send_notify("Server is full, trying to redirect..."); */
- } else {
- SILC_LOG_ERROR(("Refusing connection, server is full"));
- server->stat.conn_failures++;
- }
+ SILC_LOG_ERROR(("Refusing connection, server is full"));
+ server->stat.conn_failures++;
return;
}
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_protocol_free(protocol);
+ silc_ske_free_key_material(ctx->keymat);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (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: "
"Key exchange failed");
server->stat.auth_failures++;
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");
+ 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));
SILC_LOG_DEBUG(("Start"));
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR) {
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_protocol_free(protocol);
if (ctx->packet)
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++;
and other information is created after we have received NEW_CLIENT
packet from client. */
client = silc_idlist_add_client(server->local_list,
- NULL, NULL, NULL, NULL, NULL, sock);
+ NULL, 0, NULL, NULL, NULL, NULL, sock);
if (!client) {
SILC_LOG_ERROR(("Could not add new client to cache"));
silc_free(sock->user_data);
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);
server->stat.packets_sent++;
if (sock->outbuf->data - sock->outbuf->head)
- silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+ silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
ret = silc_server_packet_send_real(server, sock, TRUE);
/* If the closed connection was our primary router connection the
start re-connecting phase. */
- if (!server->standalone && server->server_type == SILC_SERVER &&
+ 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, 0, 500000,
+ context, 1, 0,
SILC_TASK_TIMEOUT,
SILC_TASK_PRI_NORMAL);
server);
}
+/* 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 */
+
+static int silc_server_packet_decrypt_check(SilcPacketType packet_type,
+ SilcBuffer buffer,
+ SilcPacketContext *packet,
+ void *context)
+{
+ SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
+ SilcServer server = (SilcServer)parse_ctx->context;
+
+ /* Packet is normal packet, if:
+
+ 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
+
+ all other packets are special packets
+ */
+ if ((packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
+ !(buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY)) ||
+ 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;
+
+ return FALSE;
+}
+
/* Parses whole packet, received earlier. */
SILC_TASK_CALLBACK(silc_server_packet_parse_real)
/* Decrypt the received packet */
ret = silc_packet_decrypt(parse_ctx->cipher, parse_ctx->hmac,
- packet->buffer, packet);
+ packet->buffer, packet,
+ silc_server_packet_decrypt_check, parse_ctx);
if (ret < 0)
goto out;
if (ret == SILC_PACKET_NONE)
goto out;
+ /* Check that the the current client ID is same as in the client's packet. */
+ if (sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ SilcClientEntry client = (SilcClientEntry)sock->user_data;
+ if (client && client->id) {
+ 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;
+ }
+ 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. */
switch(type) {
case SILC_PACKET_DISCONNECT:
SILC_LOG_DEBUG(("Disconnect packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
break;
case SILC_PACKET_SUCCESS:
* 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);
* 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) {
- /* XXX Audit the failure type */
- sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- sock->protocol->execute(server->timeout_queue, 0,
- sock->protocol, sock->sock, 0, 0);
+ 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;
case SILC_PACKET_REJECT:
SILC_LOG_DEBUG(("Reject packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
return;
break;
* router. Server then relays the notify messages to clients if needed.
*/
SILC_LOG_DEBUG(("Notify packet"));
- silc_server_notify(server, sock, packet);
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_notify_list(server, sock, packet);
+ else
+ silc_server_notify(server, sock, packet);
break;
/*
* specially.
*/
SILC_LOG_DEBUG(("Channel Message packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_channel_message(server, sock, packet);
break;
* 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;
* 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;
* 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;
* 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;
/*
* Private message key packet.
*/
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_private_message_key(server, sock, packet);
break;
/*
*/
case SILC_PACKET_KEY_EXCHANGE:
SILC_LOG_DEBUG(("KE packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
case SILC_PACKET_KEY_EXCHANGE_1:
SILC_LOG_DEBUG(("KE 1 packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
case SILC_PACKET_KEY_EXCHANGE_2:
SILC_LOG_DEBUG(("KE 2 packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
* 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);
break;
/*
/* 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;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
* SILC network.
*/
SILC_LOG_DEBUG(("New ID packet"));
- silc_server_new_id(server, sock, packet);
- break;
-
- case SILC_PACKET_NEW_ID_LIST:
- /*
- * Received list of ID's. This packet is used by servers and routers
- * to notify their primary router about clients and servers they have.
- */
- SILC_LOG_DEBUG(("New ID List packet"));
- silc_server_new_id_list(server, sock, packet);
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_new_id_list(server, sock, packet);
+ else
+ silc_server_new_id(server, sock, packet);
break;
case SILC_PACKET_NEW_CLIENT:
* 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);
break;
* connected to us.
*/
SILC_LOG_DEBUG(("New Server packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_new_server(server, sock, packet);
break;
* network are distributed using this packet.
*/
SILC_LOG_DEBUG(("New Channel packet"));
- silc_server_new_channel(server, sock, packet);
- break;
-
- case SILC_PACKET_NEW_CHANNEL_USER:
- /*
- * Received new channel user packet. Information about new users on a
- * channel are distributed between routers using this packet. The
- * router receiving this will redistribute it and also sent JOIN notify
- * to local clients on the same channel. Normal server sends JOIN notify
- * to its local clients on the channel.
- */
- SILC_LOG_DEBUG(("New Channel User packet"));
- silc_server_new_channel_user(server, sock, packet);
- break;
-
- case SILC_PACKET_NEW_CHANNEL_LIST:
- /*
- * List of new channel packets received. This is usually received when
- * existing server or router connects to us and distributes information
- * of all channels it has.
- */
- SILC_LOG_DEBUG(("New Channel List packet"));
- silc_server_new_channel_list(server, sock, packet);
- break;
-
- case SILC_PACKET_NEW_CHANNEL_USER_LIST:
- /*
- * List of new channel user packets received. This is usually received
- * when existing server or router connects to us and distributes
- * information of all channel users it has.
- */
- SILC_LOG_DEBUG(("New Channel User List packet"));
- silc_server_new_channel_user_list(server, sock, 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;
- case SILC_PACKET_REPLACE_ID:
- /*
- * Received replace ID packet. This sends the old ID that is to be
- * replaced with the new one included into the packet. Client must not
- * send this packet.
- */
- SILC_LOG_DEBUG(("Replace ID packet"));
- silc_server_replace_id(server, sock, packet);
- break;
-
- case SILC_PACKET_REMOVE_ID:
- /*
- * Received remove ID Packet.
- */
- SILC_LOG_DEBUG(("Remove ID packet"));
- silc_server_remove_id(server, sock, packet);
- break;
-
- case SILC_PACKET_REMOVE_CHANNEL_USER:
- /*
- * Received packet to remove user from a channel. Routers notify other
- * routers about a user leaving a channel.
- */
- SILC_LOG_DEBUG(("Remove Channel User packet"));
- silc_server_remove_channel_user(server, sock, packet);
- break;
-
- case SILC_PACKET_SET_MODE:
+ case SILC_PACKET_HEARTBEAT:
/*
- * Received packet to set the mode of channel or client's channel mode.
+ * Received heartbeat.
*/
- SILC_LOG_DEBUG(("Set Mode packet"));
- silc_server_set_mode(server, sock, packet);
+ SILC_LOG_DEBUG(("Heartbeat packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
break;
- case SILC_PACKET_HEARTBEAT:
+ case SILC_PACKET_KEY_AGREEMENT:
/*
* Received heartbeat.
*/
- SILC_LOG_DEBUG(("Heartbeat packet"));
+ SILC_LOG_DEBUG(("Key agreement packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_key_agreement(server, sock, packet);
break;
default:
}
+/* Creates connection to a remote router. */
+
+void silc_server_create_connection(SilcServer server,
+ char *remote_host, unsigned int port)
+{
+ SilcServerConnection sconn;
+
+ /* 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;
+
+ silc_task_register(server->timeout_queue, 0,
+ silc_server_connect_router,
+ (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+}
+
/* Closes connection to socket connection */
void silc_server_close_connection(SilcServer server,
SILC_LOG_DEBUG(("Disconnecting remote host"));
- SILC_LOG_INFO(("Disconnecting %s:%d (%s) [%s]", sock->hostname,
+ 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" :
silc_server_close_connection(server, sock);
}
-/* Free's user_data pointer from socket connection object. This also sends
+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,
+ int notify,
+ 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_server_packet_send_real(server, sock, TRUE);
+
+ SILC_SET_CONNECTION_FOR_INPUT(sock->sock);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
+ }
+
+ /* Send SIGNOFF notify to routers. */
+ if (notify && !server->standalone && server->router)
+ silc_server_send_notify_signoff(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, client->id,
+ SILC_ID_CLIENT_LEN, signoff);
+
+ /* Remove client from all channels */
+ if (notify)
+ silc_server_remove_from_channels(server, NULL, client,
+ TRUE, signoff, TRUE);
+ else
+ silc_server_remove_from_channels(server, NULL, client,
+ FALSE, NULL, FALSE);
+
+ /* We will not delete the client entry right away. We will take it
+ into history (for WHOWAS command) for 5 minutes */
+ 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 */
+ server->stat.my_clients--;
+ server->stat.clients--;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_clients--;
+}
+
+/* Frees user_data pointer from socket connection object. This also sends
appropriate notify packets to the network to inform about leaving
entities. */
case SILC_SOCKET_TYPE_CLIENT:
{
SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-
- /* Remove client from all channels */
- silc_server_remove_from_channels(server, sock, user_data);
-
- /* XXX must take some info to history before freeing */
-
- /* Send REMOVE_ID packet to routers. */
- if (!server->standalone && server->router)
- silc_server_send_remove_id(server, server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE, user_data->id,
- SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
-
- /* Free the client entry and everything in it */
- silc_idlist_del_data(user_data);
- silc_idlist_del_client(server->local_list, user_data);
- server->stat.my_clients--;
- server->stat.clients--;
- if (server->server_type == SILC_ROUTER)
- server->stat.cell_clients--;
+ silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
break;
}
case SILC_SOCKET_TYPE_SERVER:
/* Send REMOVE_ID packet to routers. */
if (!server->standalone && server->router)
- silc_server_send_remove_id(server, server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE, user_data->id,
- SILC_ID_SERVER_LEN, SILC_ID_SERVER);
+ silc_server_send_notify_server_signoff(server,
+ server->router->connection,
+ server->server_type ==
+ SILC_SERVER ?
+ FALSE : TRUE, user_data->id,
+ SILC_ID_SERVER_LEN);
/* Then also free all client entries that this server owns as
they will become invalid now as well. */
/* If this was our primary router connection then we're lost to
the outside world. */
- if (server->server_type == SILC_SERVER && server->router == user_data) {
+ if (server->router == user_data) {
server->id_entry->router = NULL;
server->router = NULL;
server->standalone = TRUE;
SilcIDCacheEntry id_cache = NULL;
SilcClientEntry client = NULL;
+ SILC_LOG_DEBUG(("Start"));
+
if (silc_idcache_find_by_id(server->local_list->clients,
SILC_ID_CACHE_ANY, SILC_ID_CLIENT, &list)) {
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->local_list, client);
if (!silc_idcache_list_next(list, &id_cache))
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client);
+ silc_server_remove_from_channels(server, NULL, client, TRUE,
+ NULL, TRUE);
silc_idlist_del_client(server->global_list, client);
if (!silc_idcache_list_next(list, &id_cache))
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
- SilcClientEntry client)
+ SilcClientEntry client,
+ int notify,
+ char *signoff_message,
+ int keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry 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 (channel->global_users)
+ if (notify && channel->global_users)
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_SIGNOFF, 1,
- clidp->data, clidp->len);
+ SILC_NOTIFY_TYPE_SIGNOFF,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
if (!silc_idlist_del_channel(server->local_list, channel))
silc_idlist_del_channel(server->global_list, channel);
/* Send notify to channel about client leaving SILC and thus
the entire channel. */
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_SIGNOFF, 1,
- clidp->data, clidp->len);
+ if (notify)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_SIGNOFF,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
+
+ if (keygen) {
+ /* Re-generate channel key */
+ silc_server_create_channel_key(server, channel, 0);
+
+ /* Send the channel key to the channel. The key of course is not sent
+ to the client who was removed f rom the channel. */
+ silc_server_send_channel_key(server, client->connection, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
}
silc_buffer_free(clidp);
SilcChannelEntry silc_server_create_new_channel(SilcServer server,
SilcServerID *router_id,
char *cipher,
+ char *hmac,
char *channel_name,
int broadcast)
{
SilcChannelID *channel_id;
SilcChannelEntry entry;
SilcCipher key;
+ SilcHmac newhmac;
SILC_LOG_DEBUG(("Creating new channel"));
if (!cipher)
- cipher = "twofish";
+ cipher = "aes-256-cbc";
+ if (!hmac)
+ hmac = "hmac-sha1-96";
/* Allocate cipher */
- silc_cipher_alloc(cipher, &key);
+ if (!silc_cipher_alloc(cipher, &key))
+ return NULL;
+
+ /* Allocate hmac */
+ if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+ silc_cipher_free(key);
+ return NULL;
+ }
channel_name = strdup(channel_name);
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);
+ NULL, key, newhmac);
if (!entry) {
silc_free(channel_name);
return NULL;
}
+ entry->cipher = strdup(cipher);
+ entry->hmac_name = strdup(hmac);
+
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry, 16);
+ silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(key) / 8);
/* Notify other routers about the new channel. We send the packet
to our primary route. */
- if (broadcast && server->standalone == FALSE) {
+ if (broadcast && server->standalone == FALSE)
silc_server_send_new_channel(server, server->router->connection, TRUE,
- channel_name, entry->id, SILC_ID_CHANNEL_LEN);
- }
+ channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+ entry->mode);
server->stat.my_channels++;
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;
+ SilcHmac newhmac;
SILC_LOG_DEBUG(("Creating new channel"));
if (!cipher)
- cipher = "twofish";
+ cipher = "aes-256-cbc";
+ if (!hmac)
+ hmac = "hmac-sha1-96";
/* Allocate cipher */
- silc_cipher_alloc(cipher, &key);
+ if (!silc_cipher_alloc(cipher, &key))
+ return NULL;
+
+ /* Allocate hmac */
+ if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+ silc_cipher_free(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);
+ NULL, key, newhmac);
if (!entry) {
silc_free(channel_name);
return NULL;
}
/* Now create the actual key material */
- silc_server_create_channel_key(server, entry, 16);
+ silc_server_create_channel_key(server, entry,
+ silc_cipher_get_key_len(key) / 8);
/* Notify other routers about the new channel. We send the packet
to our primary route. */
- if (broadcast && server->standalone == FALSE) {
+ if (broadcast && server->standalone == FALSE)
silc_server_send_new_channel(server, server->router->connection, TRUE,
- channel_name, entry->id, SILC_ID_CHANNEL_LEN);
- }
+ channel_name, entry->id, SILC_ID_CHANNEL_LEN,
+ entry->mode);
server->stat.my_channels++;
unsigned int key_len)
{
int i;
- unsigned char channel_key[32];
+ unsigned char channel_key[32], hash[32];
unsigned int len;
+ SILC_LOG_DEBUG(("Generating channel key"));
+
if (!channel->channel_key)
- silc_cipher_alloc("twofish", &channel->channel_key);
+ if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
+ return;
if (key_len)
len = key_len;
else if (channel->key_len)
len = channel->key_len / 8;
else
- len = sizeof(channel_key);
+ len = silc_cipher_get_key_len(channel->channel_key) / 8;
/* Create channel key */
for (i = 0; i < len; i++) channel_key[i] = silc_rng_get_byte(server->rng);
/* Set the key */
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- channel_key, len);
+ silc_cipher_set_key(channel->channel_key, channel_key, len * 8);
/* Remove old key if exists */
if (channel->key) {
channel->key = silc_calloc(len, sizeof(*channel->key));
memcpy(channel->key, 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));
+ memset(hash, 0, sizeof(hash));
}
/* Saves the channel key found in the encoded `key_payload' buffer. This
{
SilcChannelKeyPayload payload = NULL;
SilcChannelID *id = NULL;
- unsigned char *tmp;
+ unsigned char *tmp, hash[32];
unsigned int tmp_len;
char *cipher;
goto out;
}
- cipher = silc_channel_key_get_cipher(payload, NULL);;
+ cipher = silc_channel_key_get_cipher(payload, NULL);
if (!cipher) {
channel = NULL;
goto out;
goto out;
}
+ if (channel->cipher)
+ silc_free(channel->cipher);
+ channel->cipher = strdup(cipher);
+
/* Save the key */
channel->key_len = tmp_len * 8;
channel->key = silc_calloc(tmp_len, sizeof(unsigned char));
memcpy(channel->key, tmp, tmp_len);
- channel->channel_key->cipher->set_key(channel->channel_key->context,
- tmp, tmp_len);
+ silc_cipher_set_key(channel->channel_key, tmp, channel->key_len);
+
+ /* 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));
+
+ memset(hash, 0, sizeof(hash));
+ memset(tmp, 0, tmp_len);
out:
if (id)
/* Send the packet */
silc_server_packet_send(server, server->router->connection,
- SILC_PACKET_NEW_ID_LIST, 0,
+ SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
servers->data, servers->len, TRUE);
silc_buffer_free(servers);
/* Send the packet */
silc_server_packet_send(server, server->router->connection,
- SILC_PACKET_NEW_ID_LIST, 0,
+ SILC_PACKET_NEW_ID, SILC_PACKET_FLAG_LIST,
clients->data, clients->len, TRUE);
silc_buffer_free(clients);
}
}
+static SilcBuffer
+silc_server_announce_encode_join(unsigned int argc, ...)
+{
+ va_list ap;
+
+ va_start(ap, argc);
+ return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
+}
+
/* Returns assembled packets for all channels and users on those channels
from the given ID List. The packets are in the form dictated by the
New Channel and New Channel User payloads. */
SilcIDCacheEntry id_cache;
SilcChannelEntry channel;
SilcChannelClientEntry chl;
+ SilcBuffer chidp;
unsigned char *cid;
unsigned short name_len;
int len;
cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
name_len = strlen(channel->channel_name);
- len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+ len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
*channels =
silc_buffer_realloc(*channels,
(*channels ? (*channels)->truelen + len : len));
name_len),
SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_INT(0),
SILC_STR_END);
silc_buffer_pull(*channels, len);
/* 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) {
- unsigned char *clid;
+ SilcBuffer clidp;
+ SilcBuffer tmp;
- clid = silc_id_id2str(chl->client->id, SILC_ID_CLIENT);
-
- len = 4 + SILC_ID_CHANNEL_LEN + SILC_ID_CLIENT_LEN;
+ 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 ?
silc_buffer_pull_tail(*channel_users,
((*channel_users)->end -
(*channel_users)->data));
- silc_buffer_format(*channel_users,
- SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
- SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
- SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
- SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
- SILC_STR_END);
+
+ silc_buffer_put(*channel_users, tmp->data, tmp->len);
silc_buffer_pull(*channel_users, len);
- silc_free(clid);
+ silc_buffer_free(clidp);
+ silc_buffer_free(tmp);
}
+ silc_buffer_free(chidp);
silc_free(cid);
/* Send the packet */
silc_server_packet_send(server, server->router->connection,
- SILC_PACKET_NEW_CHANNEL_LIST, 0,
+ SILC_PACKET_NEW_CHANNEL, SILC_PACKET_FLAG_LIST,
channels->data, channels->len,
FALSE);
/* Send the packet */
silc_server_packet_send(server, server->router->connection,
- SILC_PACKET_NEW_CHANNEL_USER_LIST, 0,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
channel_users->data, channel_users->len,
FALSE);
silc_buffer_free(channel_users);
}
}
+
+/* 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). */
+
+SILC_TASK_CALLBACK(silc_server_failure_callback)
+{
+ SilcServerFailureContext f = (SilcServerFailureContext)context;
+
+ 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_free(f);
+}
+
+/* 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,
+ unsigned int *user_count)
+{
+ SilcChannelClientEntry chl;
+ SilcBuffer client_id_list;
+ SilcBuffer client_mode_list;
+ SilcBuffer idp;
+ unsigned int 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) {
+ /* 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_free(idp);
+
+ /* Client's mode on channel */
+ SILC_PUT32_MSB(chl->mode, client_mode_list->data);
+ silc_buffer_pull(client_mode_list, 4);
+
+ list_count++;
+ }
+ silc_buffer_push(client_id_list,
+ client_id_list->data - client_id_list->head);
+ 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;
+}
+
+/* Saves users and their modes to the `channel'. */
+
+void silc_server_save_users_on_channel(SilcServer server,
+ SilcSocketConnection sock,
+ SilcChannelEntry channel,
+ SilcClientID *noadd,
+ SilcBuffer user_list,
+ SilcBuffer mode_list,
+ unsigned int user_count)
+{
+ int i;
+
+ /* 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++) {
+ unsigned short idp_len;
+ unsigned int mode;
+ SilcClientID *client_id;
+ SilcClientEntry client;
+
+ /* 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)
+ continue;
+
+ /* 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);
+ continue;
+ }
+
+ /* Check if we have this client cached already. */
+ 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) {
+ /* 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);
+ 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
+ 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);
+ if (!client) {
+ silc_free(client_id);
+ continue;
+ }
+
+ client->data.registered = TRUE;
+ }
+
+ silc_free(client_id);
+
+ if (!silc_server_client_on_channel(client, channel)) {
+ /* Client was not on the channel, add it. */
+ SilcChannelClientEntry 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);
+ }
+ }
+}
+
+/* Lookups route to the client indicated by `id' client ID. The connection
+ object and internal data object is returned. Returns NULL if route
+ could not be found to the client. 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,
+ unsigned int id_len,
+ SilcClientID *client_id,
+ SilcIDListData *idata)
+{
+ SilcClientID *id;
+ SilcClientEntry client;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* 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"));
+ return NULL;
+ }
+ } 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);
+ if (client) {
+ silc_free(id);
+
+ if (client && client->data.registered == FALSE)
+ return NULL;
+
+ /* If we are router and the client has router then the client is in
+ our cell but not directly connected to us. */
+ if (server->server_type == SILC_ROUTER && client->router) {
+ /* We are of course in this case the client's router thus the real
+ "router" of the client is the server who owns the client. Thus
+ we will send the packet to that server. */
+ *idata = (SilcIDListData)client->router;
+ return client->router->connection;
+ }
+
+ /* Seems that client really is directly connected to us */
+ *idata = (SilcIDListData)client;
+ return client->connection;
+ }
+
+ /* Destination belongs to someone not in this server. If we are normal
+ server our action is to send the packet to our router. */
+ if (server->server_type == SILC_SERVER && !server->standalone) {
+ silc_free(id);
+ *idata = (SilcIDListData)server->router;
+ return server->router->connection;
+ }
+
+ /* We are router and we will perform route lookup for the destination
+ and send the packet to fastest route. */
+ if (server->server_type == SILC_ROUTER && !server->standalone) {
+ /* Check first that the ID is valid */
+ client = silc_idlist_find_client_by_id(server->global_list, id, NULL);
+ if (client) {
+ SilcSocketConnection dst_sock;
+
+ dst_sock = silc_server_route_get(server, id, SILC_ID_CLIENT);
+
+ silc_free(id);
+ *idata = (SilcIDListData)dst_sock->user_data;
+ return dst_sock;
+ }
+ }
+
+ silc_free(id);
+ return NULL;
+}
+
+/* Encodes and returns channel list of channels the `client' has joined.
+ Secret channels are not put to the list. */
+
+SilcBuffer silc_server_get_client_channel_list(SilcServer server,
+ SilcClientEntry client)
+{
+ SilcBuffer buffer = NULL;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ unsigned char *cid;
+ unsigned short name_len;
+ int len;
+
+ silc_list_start(client->channels);
+ while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
+ channel = chl->channel;
+
+ if (channel->mode & SILC_CHANNEL_MODE_SECRET)
+ continue;
+
+ cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ name_len = strlen(channel->channel_name);
+
+ len = 4 + name_len + SILC_ID_CHANNEL_LEN + 4;
+ buffer = silc_buffer_realloc(buffer,
+ (buffer ? (buffer)->truelen + len : len));
+ silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+ silc_buffer_format(buffer,
+ SILC_STR_UI_SHORT(name_len),
+ SILC_STR_UI_XNSTRING(channel->channel_name,
+ name_len),
+ SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_INT(chl->mode), /* Client's mode */
+ SILC_STR_END);
+ silc_buffer_pull(buffer, len);
+ silc_free(cid);
+ }
+
+ if (buffer)
+ silc_buffer_push(buffer, buffer->data - buffer->head);
+
+ return buffer;
+}
+
+/* Finds client entry by Client ID and if it is not found then resolves
+ it using WHOIS command. */
+
+SilcClientEntry silc_server_get_client_resolve(SilcServer server,
+ SilcClientID *client_id)
+{
+ 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);
+ }
+
+ return client;
+}