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);
+SILC_TASK_CALLBACK(silc_server_rekey_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
int *sock = NULL, sock_count = 0, i;
SilcServerID *id;
SilcServerEntry id_entry;
+ SilcIDListPurge purge;
SILC_LOG_DEBUG(("Initializing server"));
assert(server);
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();
{
unsigned char *public_key;
unsigned char *private_key;
- unsigned int pk_len, prv_len;
+ uint32 pk_len, prv_len;
struct stat st;
if (stat("pubkey.pub", &st) < 0 && stat("privkey.prv", &st) < 0) {
}
/* 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 */
(SilcServerKEInternalContext *)protocol->context;
SilcServer server = (SilcServer)ctx->server;
SilcServerConnection sconn = (SilcServerConnection)ctx->context;
- SilcSocketConnection sock = NULL;
+ SilcSocketConnection sock = server->sockets[fd];
SilcServerConnAuthInternalContext *proto_ctx;
+ SilcServerConfigSectionServerConnection *conn = NULL;
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);
+ sock->protocol = NULL;
+ 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);
+ sock->protocol = NULL;
+ 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->sock = sock;
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 */
- proto_ctx->auth_meth = SILC_AUTH_PASSWORD;
- if (server->config->routers) {
- SilcServerConfigSectionServerConnection *conn = NULL;
-
- /* 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 {
- /* 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);
+ sock->protocol = NULL;
+ 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 */
SilcServerEntry id_entry;
SilcBuffer packet;
SilcServerHBContext hb_context;
+ SilcServerRekeyContext rekey;
unsigned char *id_string;
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 */
if (ctx->dest_id)
silc_free(ctx->dest_id);
silc_server_perform_heartbeat,
server->timeout_queue);
+ /* Registed re-key timeout */
+ rekey = silc_calloc(1, sizeof(*rekey));
+ rekey->server = server;
+ rekey->sock = sock;
+ rekey->timeout = 3600; /* XXX hardcoded */
+ silc_task_register(server->timeout_queue, sock->sock,
+ silc_server_rekey_callback,
+ (void *)rekey, rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
/* If we are router then announce our possible servers. */
if (server->server_type == SILC_ROUTER)
silc_server_announce_servers(server);
/* 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;
}
SilcServerKEInternalContext *ctx =
(SilcServerKEInternalContext *)protocol->context;
SilcServer server = (SilcServer)ctx->server;
- SilcSocketConnection sock = NULL;
+ SilcSocketConnection sock = server->sockets[fd];
SilcServerConnAuthInternalContext *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);
+ sock->protocol = NULL;
+ 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);
+ sock->protocol = NULL;
+ 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->sock = sock;
proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */
proto_ctx->responder = TRUE;
proto_ctx->dest_id_type = ctx->dest_id_type;
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);
+ sock->protocol = NULL;
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
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))
+ 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;
+
+ 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. */
silc_server_packet_parse_type(server, sock, packet);
out:
- silc_buffer_clear(sock->inbuf);
+ /* silc_buffer_clear(sock->inbuf); */
silc_packet_context_free(packet);
silc_free(parse_ctx);
}
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;
/*
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
- if (sock->protocol && sock->protocol->protocol->type
- == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+ if (sock->protocol && sock->protocol->protocol &&
+ sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
SilcServerKEInternalContext *proto_ctx =
(SilcServerKEInternalContext *)sock->protocol->context;
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
- if (sock->protocol && sock->protocol->protocol->type
- == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+ if (sock->protocol && sock->protocol->protocol &&
+ sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
SilcServerKEInternalContext *proto_ctx =
(SilcServerKEInternalContext *)sock->protocol->context;
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
- if (sock->protocol && sock->protocol->protocol->type
- == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
+ if (sock->protocol && sock->protocol->protocol &&
+ sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
SilcServerKEInternalContext *proto_ctx =
(SilcServerKEInternalContext *)sock->protocol->context;
SILC_LOG_DEBUG(("Connection authentication request packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
+ silc_server_connection_auth_request(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;
+
+ case SILC_PACKET_REKEY:
+ /*
+ * Received re-key packet. The sender wants to regenerate the session
+ * keys.
+ */
+ SILC_LOG_DEBUG(("Re-key packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_rekey(server, sock, packet);
+ break;
+
+ case SILC_PACKET_REKEY_DONE:
+ /*
+ * The re-key is done.
+ */
+ SILC_LOG_DEBUG(("Re-key done packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
+ if (sock->protocol && sock->protocol->protocol &&
+ sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) {
+
+ SilcServerRekeyInternalContext *proto_ctx =
+ (SilcServerRekeyInternalContext *)sock->protocol->context;
+
+ if (proto_ctx->packet)
+ silc_packet_context_free(proto_ctx->packet);
+
+ proto_ctx->packet = silc_packet_context_dup(packet);
+
+ /* Let the protocol handle the packet */
+ sock->protocol->execute(server->timeout_queue, 0,
+ sock->protocol, sock->sock,
+ 0, 100000);
+ } else {
+ SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
+ "protocol active, packet dropped."));
+ }
+ 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)
+ char *remote_host, uint32 port)
{
SilcServerConnection sconn;
SILC_TASK_PRI_NORMAL);
}
+SILC_TASK_CALLBACK(silc_server_close_connection_final)
+{
+ silc_socket_free((SilcSocketConnection)context);
+}
+
/* Closes connection to socket connection */
void silc_server_close_connection(SilcServer server,
/* Close the actual connection */
silc_net_close_connection(sock->sock);
server->sockets[sock->sock] = NULL;
- silc_socket_free(sock);
+
+ silc_task_register(server->timeout_queue, 0,
+ silc_server_close_connection_final,
+ (void *)sock, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
/* Sends disconnect message to remote connection and disconnects the
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 user_data, char *signoff)
+ SilcClientEntry client,
+ int notify,
+ char *signoff)
{
- /* Send REMOVE_ID packet to routers. */
- if (!server->standalone && server->router)
+ 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, user_data->id,
+ FALSE : TRUE, client->id,
SILC_ID_CLIENT_LEN, signoff);
/* Remove client from all channels */
- silc_server_remove_from_channels(server, sock, user_data, signoff);
+ 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);
- /* XXX must take some info to history before freeing */
+ /* 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 */
- 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)
case SILC_SOCKET_TYPE_CLIENT:
{
SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
- silc_server_free_client_data(server, sock, user_data, NULL);
+ silc_server_free_client_data(server, sock, user_data, TRUE, NULL);
break;
}
case SILC_SOCKET_TYPE_SERVER:
{
SilcServerEntry user_data = (SilcServerEntry)sock->user_data;
- /* Send REMOVE_ID packet to routers. */
- if (!server->standalone && server->router)
- 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. */
- silc_server_remove_clients_by_server(server, user_data);
+ /* 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);
/* 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;
/* 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'. */
+ 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. */
int silc_server_remove_clients_by_server(SilcServer server,
- SilcServerEntry entry)
+ 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(("Start"));
+ 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 (silc_idcache_find_by_id(server->local_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;
-
+ 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++;
+ }
+
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, NULL);
+ silc_server_remove_from_channels(server, NULL, client, FALSE,
+ NULL, FALSE);
silc_idlist_del_client(server->local_list, client);
if (!silc_idcache_list_next(list, &id_cache))
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++;
+ }
+
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, NULL);
+ silc_server_remove_from_channels(server, NULL, client, FALSE,
+ NULL, FALSE);
silc_idlist_del_client(server->global_list, client);
if (!silc_idcache_list_next(list, &id_cache))
}
silc_idcache_list_free(list);
}
-
+
+ /* Send the SERVER_SIGNOFF notify */
+ if (server_signoff) {
+ SilcBuffer args;
+
+ /* 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);
+ }
+
+ 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_free(clients);
+ silc_buffer_free(args);
+ silc_free(argv);
+ silc_free(argv_lens);
+ silc_free(argv_types);
+ }
+
return TRUE;
}
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
SilcClientEntry client,
- char *signoff_message)
+ int notify,
+ char *signoff_message,
+ int keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry 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);
- server->stat.my_channels--;
continue;
}
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,
signoff_message ? 2 : 1,
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);
- server->stat.my_channels--;
continue;
}
/* 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,
- signoff_message ? 2 : 1,
- clidp->data, clidp->len,
- signoff_message, signoff_message ?
- strlen(signoff_message) : 0);
+ 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 && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ /* 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 from the channel. */
+ silc_server_send_channel_key(server, client->connection, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
}
silc_buffer_free(clidp);
/* 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);
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;
+
+ 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);
- silc_buffer_free(clidp);
- server->stat.my_channels--;
return FALSE;
}
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 = "aes-256-cbc";
+ if (!hmac)
+ hmac = "hmac-sha1-96";
/* Allocate cipher */
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 */
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, 32);
+ 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 = "aes-256-cbc";
+ if (!hmac)
+ hmac = "hmac-sha1-96";
/* Allocate cipher */
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, 32);
+ 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++;
return entry;
}
+/* Channel's key re-key timeout callback. */
+
+SILC_TASK_CALLBACK(silc_server_channel_key_rekey)
+{
+ 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);
+
+ silc_task_register(server->timeout_queue, 0,
+ silc_server_channel_key_rekey,
+ (void *)rekey, 3600, 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+}
+
/* 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,
- unsigned int key_len)
+ uint32 key_len)
{
int i;
- unsigned char channel_key[32];
- unsigned int len;
+ unsigned char channel_key[32], hash[32];
+ uint32 len;
+
+ SILC_LOG_DEBUG(("Generating channel key"));
+
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+ SILC_LOG_DEBUG(("Channel has private keys, will not generate new key"));
+ return;
+ }
if (!channel->channel_key)
if (!silc_cipher_alloc("aes-256-cbc", &channel->channel_key))
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));
+
+ 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);
+ }
}
/* Saves the channel key found in the encoded `key_payload' buffer. This
{
SilcChannelKeyPayload payload = NULL;
SilcChannelID *id = NULL;
- unsigned char *tmp;
- unsigned int tmp_len;
+ unsigned char *tmp, hash[32];
+ uint32 tmp_len;
char *cipher;
+ SILC_LOG_DEBUG(("Start"));
+
/* Decode channel key payload */
payload = silc_channel_key_payload_parse(key_payload);
if (!payload) {
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);
+
+ 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);
+ }
out:
if (id)
}
static SilcBuffer
-silc_server_announce_encode_join(unsigned int argc, ...)
+silc_server_announce_encode_join(uint32 argc, ...)
{
va_list ap;
return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
}
+/* Returns assembled packets for channel users of the `channel'. */
+
+void silc_server_announce_get_channel_users(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *channel_users)
+{
+ SilcChannelClientEntry chl;
+ SilcBuffer chidp, clidp;
+ SilcBuffer tmp;
+ int len;
+
+ 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) {
+ 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 -
+ (*channel_users)->data));
+
+ silc_buffer_put(*channel_users, tmp->data, tmp->len);
+ silc_buffer_pull(*channel_users, len);
+ silc_buffer_free(clidp);
+ silc_buffer_free(tmp);
+ }
+ silc_buffer_free(chidp);
+}
+
/* 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. */
-static void silc_server_announce_get_channels(SilcServer server,
- SilcIDList id_list,
- SilcBuffer *channels,
- SilcBuffer *channel_users)
+void silc_server_announce_get_channels(SilcServer server,
+ SilcIDList id_list,
+ SilcBuffer *channels,
+ SilcBuffer *channel_users)
{
SilcIDCacheList list;
SilcIDCacheEntry id_cache;
SilcChannelEntry channel;
- SilcChannelClientEntry chl;
- SilcBuffer chidp;
unsigned char *cid;
- unsigned short name_len;
+ uint16 name_len;
int len;
+ 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)) {
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(channel->mode),
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) {
- SilcBuffer clidp;
- SilcBuffer tmp;
-
- 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 -
- (*channel_users)->data));
-
- silc_buffer_put(*channel_users, tmp->data, tmp->len);
- silc_buffer_pull(*channel_users, len);
- silc_buffer_free(clidp);
- silc_buffer_free(tmp);
- }
- silc_buffer_free(chidp);
+ silc_server_announce_get_channel_users(server, channel,
+ channel_users);
silc_free(cid);
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,
+ uint32 *user_count)
+{
+ SilcChannelClientEntry chl;
+ 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) {
+ /* 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,
+ uint32 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++) {
+ uint16 idp_len;
+ uint32 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,
+ uint32 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;
+ uint16 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;
+}
+
+/* A timeout callback for the re-key. We will be the initiator of the
+ re-key protocol. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_callback)
+{
+ SilcServerRekeyContext rekey = (SilcServerRekeyContext)context;
+ SilcServer server = rekey->server;
+ SilcProtocol protocol;
+ SilcServerRekeyInternalContext *proto_ctx;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ /* Allocate internal protocol context. This is sent as context
+ to the protocol. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = (void *)server;
+ proto_ctx->context = context;
+ proto_ctx->sock = rekey->sock;
+ proto_ctx->responder = FALSE;
+
+ /* Perform rekey protocol. Will call the final callback after the
+ protocol is over. */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_REKEY,
+ &protocol, proto_ctx, silc_server_rekey_final);
+ rekey->sock->protocol = protocol;
+
+ /* Run the protocol */
+ protocol->execute(server->timeout_queue, 0, protocol,
+ rekey->sock->sock, 0, 0);
+
+ /* Re-register re-key timeout */
+ silc_task_register(server->timeout_queue, 0,
+ silc_server_rekey_callback,
+ context, rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+/* The final callback for the REKEY protocol. This will actually take the
+ new key material into use. */
+
+SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
+{
+ SilcProtocol protocol = (SilcProtocol)context;
+ SilcServerRekeyInternalContext *ctx =
+ (SilcServerRekeyInternalContext *)protocol->context;
+ SilcServer server = (SilcServer)ctx->server;
+ SilcSocketConnection sock = ctx->sock;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
+ protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
+ /* Error occured during protocol */
+ silc_protocol_free(protocol);
+ sock->protocol = NULL;
+ if (ctx->keymat)
+ silc_ske_free_key_material(ctx->keymat);
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ silc_free(ctx);
+ return;
+ }
+
+ /* Take the keys into use */
+ if (ctx->pfs == TRUE) {
+
+ } else {
+ /* Then just generate the new keys and take them into use */
+ silc_server_protocol_rekey_generate(server, ctx);
+ }
+
+ /* Cleanup */
+ silc_protocol_free(protocol);
+ sock->protocol = NULL;
+ if (ctx->keymat)
+ silc_ske_free_key_material(ctx->keymat);
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ silc_free(ctx);
+}