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
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();
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;
/* 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,
/* 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,
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);
- 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");
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));
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);
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);
- 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_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++;
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 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 (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;
*/
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
+ silc_server_private_message_key(server, sock, packet);
break;
/*
break;
break;
+ 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;
+
default:
SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
break;
}
+/* 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
+/* Frees client data and notifies about client's signoff. */
+
+void silc_server_free_client_data(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientEntry user_data, char *signoff)
+{
+ /* Send REMOVE_ID packet to routers. */
+ if (!server->standalone && server->router)
+ silc_server_send_notify_signoff(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, user_data->id,
+ SILC_ID_CLIENT_LEN, signoff);
+
+ /* Remove client from all channels */
+ silc_server_remove_from_channels(server, sock, user_data, signoff);
+
+ /* XXX must take some info to history before freeing */
+
+ /* 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--;
+}
+
+/* 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;
-
- /* Send REMOVE_ID packet to routers. */
- if (!server->standalone && server->router)
- silc_server_send_notify_signoff(server, server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE, user_data->id,
- SILC_ID_CLIENT_LEN, NULL);
-
- /* Remove client from all channels */
- silc_server_remove_from_channels(server, sock, user_data);
-
- /* XXX must take some info to history before freeing */
-
- /* 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, NULL);
break;
}
case SILC_SOCKET_TYPE_SERVER:
/* 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, NULL);
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, NULL);
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,
+ char *signoff_message)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
/* Notify about leaving client if this channel has global users. */
if (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);
+ SILC_NOTIFY_TYPE_SIGNOFF,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
}
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;
}
/* 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. */
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. */
unsigned int key_len)
{
int i;
- unsigned char channel_key[32];
+ unsigned char channel_key[32], hash[32];
unsigned int len;
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;
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)
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, NULL, NULL,
+ silc_id_dup(client_id, SILC_ID_CLIENT),
+ sock->user_data, NULL);
+ if (!client) {
+ silc_free(client_id);
+ continue;
+ }
+ }
+
+ 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);
+ }
+ }
+}