int *sock = NULL, sock_count = 0, i;
SilcServerID *id;
SilcServerEntry id_entry;
+ SilcIDListPurge purge;
SILC_LOG_DEBUG(("Initializing server"));
assert(server);
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 */
SilcServerConnection sconn = (SilcServerConnection)ctx->context;
SilcSocketConnection sock = NULL;
SilcServerConnAuthInternalContext *proto_ctx;
+ SilcServerConfigSectionServerConnection *conn = NULL;
SILC_LOG_DEBUG(("Start"));
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);
+ 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 */
/* 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);
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)
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);
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);
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 ||
+
+ 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))
SILC_LOG_DEBUG(("Connection authentication request packet"));
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
+ silc_server_connection_auth_request(server, sock, packet);
break;
/*
void silc_server_free_client_data(SilcServer server,
SilcSocketConnection sock,
- SilcClientEntry client, char *signoff)
+ SilcClientEntry client,
+ int notify,
+ char *signoff)
{
FreeClientInternal i = silc_calloc(1, sizeof(*i));
- /* Send REMOVE_ID packet to routers. */
- if (!server->standalone && server->router)
+ /* 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 */
- silc_server_remove_from_channels(server, sock, client, 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);
/* We will not delete the client entry right away. We will take it
into history (for WHOWAS command) for 5 minutes */
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:
}
/* Remove the client entry */
- silc_server_remove_from_channels(server, NULL, client, NULL);
+ 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, NULL);
+ 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,
- char *signoff_message)
+ int notify,
+ char *signoff_message,
+ int keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
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,
/* 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 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);
return NULL;
}
+ entry->cipher = strdup(cipher);
+ entry->hmac_name = strdup(hmac);
+
/* Now create the actual key material */
silc_server_create_channel_key(server, entry,
silc_cipher_get_key_len(key) / 8);
/* 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++;
/* 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 char channel_key[32], hash[32];
unsigned int 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))
return;
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));
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);
/* 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,
+ 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);
}
}
}
+
+/* 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;
+}