int *sock = NULL, sock_count = 0, i;
SilcServerID *id;
SilcServerEntry id_entry;
+ SilcIDListPurge purge;
SILC_LOG_DEBUG(("Initializing server"));
assert(server);
}
/* 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
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"));
- 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);
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);
+ 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 */
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);
/* 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_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 && 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;
/*
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:
}
/* 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;
/* 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->founder_key) {
+ /* The founder auth data exists, do not remove the channel entry */
+ SilcChannelClientEntry chl2;
+
+ silc_free(channel->id);
+ 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->founder_key) {
+ /* The founder auth data exists, do not remove the channel entry */
+ SilcChannelClientEntry chl2;
+
+ silc_free(channel->id);
+ 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 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);
SILC_NOTIFY_TYPE_LEAVE, 1,
clidp->data, clidp->len);
+ server->stat.my_channels--;
+ silc_buffer_free(clidp);
+
+ if (channel->founder_key) {
+ /* The founder auth data exists, do not remove the channel entry */
+ SilcChannelClientEntry chl2;
+
+ silc_free(channel->id);
+ 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;
}
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;
unsigned int 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));
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;
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);
/* 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;
+}