if (server->pending_commands)
silc_dlist_uninit(server->pending_commands);
- silc_math_primegen_uninit(); /* XXX */
silc_free(server);
}
}
/* Initialize random number generator for the server. */
server->rng = silc_rng_alloc();
silc_rng_init(server->rng);
- silc_math_primegen_init(); /* XXX */
+ silc_rng_global_init(server->rng);
/* Initialize hash functions for server to use */
silc_hash_alloc("md5", &server->md5hash);
{
/* Are we executing silcd as root or a regular user? */
if (geteuid()==0) {
+
+ struct passwd *pw;
+ struct group *gr;
+ char *user, *group;
+
+ if (!server->config->identity || !server->config->identity->user ||
+ !server->config->identity->group) {
+ fprintf(stderr, "Error:"
+ "\tSILC server must not be run as root. For the security of your\n"
+ "\tsystem it is strongly suggested that you run SILC under dedicated\n"
+ "\tuser account. Modify the [Identity] configuration section to run\n"
+ "\tthe server as non-root user.\n");
+ exit(1);
+ }
+
+ /* Get the values given for user and group in configuration file */
+ user=server->config->identity->user;
+ group=server->config->identity->group;
+
+ /* Check whether the user/group information is text */
+ if (atoi(user)!=0 || atoi(group)!=0) {
+ SILC_LOG_DEBUG(("Invalid user and/or group information"));
+ SILC_LOG_DEBUG(("User and/or group given as number"));
+ fprintf(stderr, "Invalid user and/or group information\n");
+ fprintf(stderr, "Please assign them as names, not numbers\n");
+ exit(1);
+ }
+
+ /* Catch the nasty incident of string "0" returning 0 from atoi */
+ if (strcmp("0", user)==0 || strcmp("0", group)==0) {
+ SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
+ fprintf(stderr, "User and/or group configured to 0. Exiting\n");
+ exit(1);
+ }
+
+ pw=getpwnam(user);
+ gr=getgrnam(group);
- struct passwd *pw;
- struct group *gr;
- char *user, *group;
-
- if (!server->config->identity->user ||
- !server->config->identity->group) {
- SILC_LOG_DEBUG(("User and/or group not set"));
- fprintf(stderr, "User and/or group not set, exiting\n");
- exit(1);
- }
-
- /* Get the values given for user and group in configuration file */
- user=server->config->identity->user;
- group=server->config->identity->group;
-
- /* Check whether the user/group information is text */
- if (atoi(user)!=0 || atoi(group)!=0) {
- SILC_LOG_DEBUG(("Invalid user and/or group information"));
- SILC_LOG_DEBUG(("User and/or group given as number"));
- fprintf(stderr, "Invalid user and/or group information\n");
- fprintf(stderr, "Please assign them as names, not numbers\n");
- exit(1);
- }
-
- /* Catch the nasty incident of string "0" returning 0 from atoi */
- if (strcmp("0", user)==0 || strcmp("0", group)==0) {
- SILC_LOG_DEBUG(("User and/or group configured to 0. Unacceptable"));
- fprintf(stderr, "User and/or group configured to 0. Exiting\n");
- exit(1);
- }
-
- pw=getpwnam(user);
- gr=getgrnam(group);
+ if (!pw) {
+ fprintf(stderr, "No such user %s found\n", user);
+ exit(1);
+ }
+ if (!gr) {
+ fprintf(stderr, "No such group %s found\n", group);
+ exit(1);
+ }
+
/* Check whether user and/or group is set to root. If yes, exit
immediately. Otherwise, setgid and setuid server to user.group */
if (gr->gr_gid==0 || pw->pw_uid==0) {
- SILC_LOG_DEBUG(("FATAL: silcd will not run at root privileges"));
- fprintf(stderr, "User and/or group not set. Please set them\n");
+ fprintf(stderr, "Error:"
+ "\tSILC server must not be run as root. For the security of your\n"
+ "\tsystem it is strongly suggested that you run SILC under dedicated\n"
+ "\tuser account. Modify the [Identity] configuration section to run\n"
+ "\tthe server as non-root user.\n");
exit(1);
} else {
/* Fork server to background, making it a daemon */
exit(0);
}
setsid();
-
- SILC_LOG_DEBUG(("Changing to group %s", group));
+
+ SILC_LOG_DEBUG(("Changing to group %s", group));
if(setgid(gr->gr_gid)==0) {
SILC_LOG_DEBUG(("Setgid to %s", group));
} else {
}
}
-
/* Stops the SILC server. This function is used to shutdown the server.
This is usually called after the scheduler has returned. After stopping
the server one should call silc_server_free. */
silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
server->sockets[sock] = newsocket;
newsocket->hostname = strdup(sconn->remote_host);
+ newsocket->ip = strdup(sconn->remote_host);
newsocket->port = sconn->remote_port;
sconn->sock = newsocket;
if (ctx->dest_id)
silc_free(ctx->dest_id);
silc_free(ctx);
- sock->protocol = NULL;
+ if (sock)
+ sock->protocol = NULL;
silc_server_disconnect_remote(server, sock, "Server closed connection: "
"Key exchange failed");
return;
SilcSocketConnection sock = ctx->sock;
SilcServerEntry id_entry;
SilcBuffer packet;
+ SilcServerHBContext hb_context;
unsigned char *id_string;
SILC_LOG_DEBUG(("Start"));
server->router = id_entry;
server->router->data.registered = TRUE;
+ /* Perform keepalive. The `hb_context' will be freed automatically
+ when finally calling the silc_socket_free function. XXX hardcoded
+ timeout!! */
+ hb_context = silc_calloc(1, sizeof(*hb_context));
+ hb_context->server = server;
+ silc_socket_set_heartbeat(sock, 600, hb_context,
+ silc_server_perform_heartbeat,
+ server->timeout_queue);
+
+ /* If we are router then announce our possible servers. */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_announce_servers(server);
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server);
+ silc_server_announce_channels(server);
+
out:
/* Free the temporary connection data context */
if (sconn)
}
if (!newsocket->hostname)
newsocket->hostname = strdup(newsocket->ip);
+ newsocket->port = silc_net_get_remote_port(sock);
SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
newsocket->ip));
(SilcServerConnAuthInternalContext *)protocol->context;
SilcServer server = (SilcServer)ctx->server;
SilcSocketConnection sock = ctx->sock;
+ SilcServerHBContext hb_context;
void *id_entry = NULL;
SILC_LOG_DEBUG(("Start"));
/* Connection has been fully established now. Everything is ok. */
SILC_LOG_DEBUG(("New connection authenticated"));
+ /* Perform keepalive. The `hb_context' will be freed automatically
+ when finally calling the silc_socket_free function. XXX hardcoded
+ timeout!! */
+ hb_context = silc_calloc(1, sizeof(*hb_context));
+ hb_context->server = server;
+ silc_socket_set_heartbeat(sock, 600, hb_context,
+ silc_server_perform_heartbeat,
+ server->timeout_queue);
+
silc_protocol_free(protocol);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
/* Packet sending */
if (type == SILC_TASK_WRITE) {
+ /* Do not send data to disconnected connection */
+ if (SILC_IS_DISCONNECTED(sock))
+ return;
+
server->stat.packets_sent++;
if (sock->outbuf->data - sock->outbuf->head)
}
SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock));
+ SILC_SET_DISCONNECTING(sock);
/* If the closed connection was our primary router connection the
start re-connecting phase. */
/* If connection is disconnecting or disconnected we will ignore
what we read. */
if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
- SILC_LOG_DEBUG(("Ignoring read data from invalid connection"));
+ SILC_LOG_DEBUG(("Ignoring read data from disonnected connection"));
return;
}
silc_server_new_id(server, sock, packet);
break;
+ case SILC_PACKET_NEW_ID_LIST:
+ /*
+ * Received list of ID's. This packet is used by servers and routers
+ * to notify their primary router about clients and servers they have.
+ */
+ SILC_LOG_DEBUG(("New ID List packet"));
+ silc_server_new_id_list(server, sock, packet);
+ break;
+
case SILC_PACKET_NEW_CLIENT:
/*
* Received new client packet. This includes client information that
* existing server or router connects to us and distributes information
* of all channels it has.
*/
+ SILC_LOG_DEBUG(("New Channel List packet"));
+ silc_server_new_channel_list(server, sock, packet);
break;
case SILC_PACKET_NEW_CHANNEL_USER_LIST:
* when existing server or router connects to us and distributes
* information of all channel users it has.
*/
+ SILC_LOG_DEBUG(("New Channel User List packet"));
+ silc_server_new_channel_user_list(server, sock, packet);
break;
case SILC_PACKET_REPLACE_ID:
silc_server_set_mode(server, sock, packet);
break;
+ case SILC_PACKET_HEARTBEAT:
+ /*
+ * Received heartbeat.
+ */
+ SILC_LOG_DEBUG(("Heartbeat packet"));
+ break;
+
default:
SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type));
break;
SILC_LOG_DEBUG(("Disconnecting remote host"));
+ SILC_LOG_INFO(("Disconnecting %s:%d (%s) [%s]", sock->hostname,
+ sock->port,
+ (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+ sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+ sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+ "Router")));
+
/* Notify remote end that the conversation is over. The notify message
is tried to be sent immediately. */
silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,
silc_server_send_remove_id(server, server->router->connection,
server->server_type == SILC_SERVER ?
FALSE : TRUE, user_data->id,
- SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
+ SILC_ID_SERVER_LEN, SILC_ID_SERVER);
+
+ /* 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);
+
+ /* 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) {
+ server->id_entry->router = NULL;
+ server->router = NULL;
+ server->standalone = TRUE;
+ }
/* Free the server entry */
silc_idlist_del_data(user_data);
sock->user_data = NULL;
}
+/* 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'. */
+
+int silc_server_remove_clients_by_server(SilcServer server,
+ SilcServerEntry entry)
+{
+ SilcIDCacheList list = NULL;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcClientEntry client = NULL;
+
+ 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->router != entry) {
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ else
+ continue;
+ }
+
+ /* Remove the client entry */
+ silc_server_remove_from_channels(server, NULL, client);
+ silc_idlist_del_client(server->local_list, client);
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+ silc_idcache_list_free(list);
+ }
+
+ if (silc_idcache_find_by_id(server->global_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->router != entry) {
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ else
+ continue;
+ }
+
+ /* Remove the client entry */
+ silc_server_remove_from_channels(server, NULL, client);
+ silc_idlist_del_client(server->global_list, client);
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+ silc_idcache_list_free(list);
+ }
+
+ return TRUE;
+}
+
/* Checks whether given channel has global users. If it does this returns
TRUE and FALSE if there is only locally connected clients on the channel. */
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
- SilcBuffer chidp, clidp;
+ SilcBuffer clidp;
SILC_LOG_DEBUG(("Start"));
silc_list_start(client->channels);
while ((chl = silc_list_get(client->channels)) != SILC_LIST_END) {
channel = chl->channel;
- chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
- /* Remove from list */
+ /* Remove channel from client's channel list */
silc_list_del(client->channels, chl);
- /* If this client is last one on the channel the channel
- is removed all together. */
- if (silc_list_count(channel->user_list) < 2) {
+ /* Remove channel if there is no users anymore */
+ if (server->server_type == SILC_ROUTER &&
+ silc_list_count(channel->user_list) < 2) {
+ if (!silc_idlist_del_channel(server->local_list, channel))
+ silc_idlist_del_channel(server->global_list, channel);
+ server->stat.my_channels--;
+ continue;
+ }
- /* However, if the channel has marked global users then the
- channel is not created locally, and this does not remove the
- channel globally from SILC network, in this case we will
- notify that this client has left the channel. */
+ /* Remove client from channel's client list */
+ silc_list_del(channel->user_list, chl);
+ silc_free(chl);
+ server->stat.my_chanclients--;
+
+ /* If there is not at least one local user on the channel then we don't
+ need the channel entry anymore, we can remove it safely. */
+ if (server->server_type == SILC_SERVER &&
+ !silc_server_channel_has_local(channel)) {
+ /* Notify about leaving client if this channel has global users. */
if (channel->global_users)
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF, 1,
clidp->data, clidp->len);
-
- silc_idlist_del_channel(server->local_list, channel);
+
+ if (!silc_idlist_del_channel(server->local_list, channel))
+ silc_idlist_del_channel(server->global_list, channel);
server->stat.my_channels--;
continue;
}
- /* Remove from list */
- silc_list_del(channel->user_list, chl);
- silc_free(chl);
- server->stat.my_chanclients--;
-
/* Send notify to channel about client leaving SILC and thus
the entire channel. */
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF, 1,
clidp->data, clidp->len);
- silc_buffer_free(chidp);
}
silc_buffer_free(clidp);
ch = chl->channel;
- /* Remove from list */
+ /* Remove channel from client's channel list */
silc_list_del(client->channels, chl);
- /* If this client is last one on the channel the channel
- is removed all together. */
- if (silc_list_count(channel->user_list) < 2) {
- /* Notify about leaving client if this channel has global users. */
- if (notify && channel->global_users)
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_LEAVE, 1,
- clidp->data, clidp->len);
-
- silc_idlist_del_channel(server->local_list, channel);
+ /* Remove channel if there is no users anymore */
+ if (server->server_type == SILC_ROUTER &&
+ silc_list_count(channel->user_list) < 2) {
+ 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;
}
- /* Remove from list */
+ /* Remove client from channel's client list */
silc_list_del(channel->user_list, chl);
silc_free(chl);
server->stat.my_chanclients--;
!silc_server_channel_has_global(channel))
channel->global_users = FALSE;
- /* If tehre is not at least one local user on the channel then we don't
+ /* If there is not at least one local user on the channel then we don't
need the channel entry anymore, we can remove it safely. */
if (server->server_type == SILC_SERVER &&
!silc_server_channel_has_local(channel)) {
- silc_idlist_del_channel(server->local_list, channel);
+ /* Notify about leaving client if this channel has global users. */
+ if (notify && channel->global_users)
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_LEAVE, 1,
+ clidp->data, clidp->len);
+
+ 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 *channel_name)
+ char *channel_name,
+ int broadcast)
{
SilcChannelID *channel_id;
SilcChannelEntry entry;
/* Notify other routers about the new channel. We send the packet
to our primary route. */
- if (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);
+ }
+
+ server->stat.my_channels++;
+
+ return entry;
+}
+
+/* Same as above but creates the channel with Channel ID `channel_id. */
+
+SilcChannelEntry
+silc_server_create_new_channel_with_id(SilcServer server,
+ char *cipher,
+ char *channel_name,
+ SilcChannelID *channel_id,
+ int broadcast)
+{
+ SilcChannelEntry entry;
+ SilcCipher key;
+
+ SILC_LOG_DEBUG(("Creating new channel"));
+
+ if (!cipher)
+ cipher = "twofish";
+
+ /* Allocate cipher */
+ silc_cipher_alloc(cipher, &key);
+
+ 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);
+ if (!entry) {
+ silc_free(channel_name);
+ return NULL;
+ }
+
+ /* Now create the actual key material */
+ silc_server_create_channel_key(server, entry, 16);
+
+ /* Notify other routers about the new channel. We send the packet
+ to our primary route. */
+ if (broadcast && server->standalone == FALSE) {
silc_server_send_new_channel(server, server->router->connection, TRUE,
channel_name, entry->id, SILC_ID_CHANNEL_LEN);
}
channel = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
if (!channel) {
- SILC_LOG_ERROR(("Received key for non-existent channel"));
- goto out;
+ channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL);
+ if (!channel) {
+ SILC_LOG_ERROR(("Received key for non-existent channel"));
+ goto out;
+ }
}
}
return channel;
}
+
+/* Heartbeat callback. This function is set as argument for the
+ silc_socket_set_heartbeat function. The library will call this function
+ at the set time interval. */
+
+void silc_server_perform_heartbeat(SilcSocketConnection sock,
+ void *hb_context)
+{
+ SilcServerHBContext hb = (SilcServerHBContext)hb_context;
+
+ SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname,
+ sock->ip));
+
+ /* Send the heartbeat */
+ silc_server_send_heartbeat(hb->server, sock);
+}
+
+/* Returns assembled of all servers in the given ID list. The packet's
+ form is dictated by the New ID payload. */
+
+static void silc_server_announce_get_servers(SilcServer server,
+ SilcIDList id_list,
+ SilcBuffer *servers)
+{
+ SilcIDCacheList list;
+ SilcIDCacheEntry id_cache;
+ SilcServerEntry entry;
+ SilcBuffer idp;
+
+ /* Go through all clients in the list */
+ if (silc_idcache_find_by_id(id_list->clients, SILC_ID_CACHE_ANY,
+ SILC_ID_SERVER, &list)) {
+ if (silc_idcache_list_first(list, &id_cache)) {
+ while (id_cache) {
+ entry = (SilcServerEntry)id_cache->context;
+
+ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+
+ *servers = silc_buffer_realloc(*servers,
+ (*servers ?
+ (*servers)->truelen + idp->len :
+ idp->len));
+ silc_buffer_pull_tail(*servers, ((*servers)->end - (*servers)->data));
+ silc_buffer_put(*servers, idp->data, idp->len);
+ silc_buffer_pull(*servers, idp->len);
+ silc_buffer_free(idp);
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+
+ silc_idcache_list_free(list);
+ }
+}
+
+/* This function is used by router to announce existing servers to our
+ primary router when we've connected to it. */
+
+void silc_server_announce_servers(SilcServer server)
+{
+ SilcBuffer servers = NULL;
+
+ SILC_LOG_DEBUG(("Announcing servers"));
+
+ /* Get servers in local list */
+ silc_server_announce_get_servers(server, server->local_list, &servers);
+
+ /* Get servers in global list */
+ silc_server_announce_get_servers(server, server->global_list, &servers);
+
+ if (servers) {
+ silc_buffer_push(servers, servers->data - servers->head);
+ SILC_LOG_HEXDUMP(("servers"), servers->data, servers->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_NEW_ID_LIST, 0,
+ servers->data, servers->len, TRUE);
+
+ silc_buffer_free(servers);
+ }
+}
+
+/* Returns assembled packet of all clients in the given ID list. The
+ packet's form is dictated by the New ID Payload. */
+
+static void silc_server_announce_get_clients(SilcServer server,
+ SilcIDList id_list,
+ SilcBuffer *clients)
+{
+ SilcIDCacheList list;
+ SilcIDCacheEntry id_cache;
+ SilcClientEntry client;
+ SilcBuffer idp;
+
+ /* Go through all clients in the list */
+ if (silc_idcache_find_by_id(id_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;
+
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+ *clients = silc_buffer_realloc(*clients,
+ (*clients ?
+ (*clients)->truelen + idp->len :
+ idp->len));
+ silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
+ silc_buffer_put(*clients, idp->data, idp->len);
+ silc_buffer_pull(*clients, idp->len);
+ silc_buffer_free(idp);
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+
+ silc_idcache_list_free(list);
+ }
+}
+
+/* This function is used to announce our existing clients to our router
+ when we've connected to it. */
+
+void silc_server_announce_clients(SilcServer server)
+{
+ SilcBuffer clients = NULL;
+
+ SILC_LOG_DEBUG(("Announcing clients"));
+
+ /* Get clients in local list */
+ silc_server_announce_get_clients(server, server->local_list,
+ &clients);
+
+ /* As router we announce our global list as well */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_announce_get_clients(server, server->global_list,
+ &clients);
+
+ if (clients) {
+ silc_buffer_push(clients, clients->data - clients->head);
+ SILC_LOG_HEXDUMP(("clients"), clients->data, clients->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_NEW_ID_LIST, 0,
+ clients->data, clients->len, TRUE);
+
+ silc_buffer_free(clients);
+ }
+}
+
+/* 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)
+{
+ SilcIDCacheList list;
+ SilcIDCacheEntry id_cache;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ unsigned char *cid;
+ unsigned short name_len;
+ int len;
+
+ /* Go through all channels in the list */
+ if (silc_idcache_find_by_id(id_list->channels, SILC_ID_CACHE_ANY,
+ SILC_ID_CHANNEL, &list)) {
+ if (silc_idcache_list_first(list, &id_cache)) {
+ while (id_cache) {
+ channel = (SilcChannelEntry)id_cache->context;
+
+ cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ name_len = strlen(channel->channel_name);
+
+ len = 4 + name_len + SILC_ID_CHANNEL_LEN;
+ *channels =
+ silc_buffer_realloc(*channels,
+ (*channels ? (*channels)->truelen + len : len));
+ silc_buffer_pull_tail(*channels,
+ ((*channels)->end - (*channels)->data));
+ silc_buffer_format(*channels,
+ 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_END);
+ silc_buffer_pull(*channels, len);
+
+ /* Now find all users on the channel */
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ unsigned char *clid;
+
+ clid = silc_id_id2str(chl->client->id, SILC_ID_CLIENT);
+
+ len = 4 + SILC_ID_CHANNEL_LEN + SILC_ID_CLIENT_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_format(*channel_users,
+ SILC_STR_UI_SHORT(SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_XNSTRING(cid, SILC_ID_CHANNEL_LEN),
+ SILC_STR_UI_SHORT(SILC_ID_CLIENT_LEN),
+ SILC_STR_UI_XNSTRING(clid, SILC_ID_CLIENT_LEN),
+ SILC_STR_END);
+ silc_buffer_pull(*channel_users, len);
+ silc_free(clid);
+ }
+
+ silc_free(cid);
+
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+
+ silc_idcache_list_free(list);
+ }
+}
+
+/* This function is used to announce our existing channels to our router
+ when we've connected to it. This also announces the users on the
+ channels to the router. */
+
+void silc_server_announce_channels(SilcServer server)
+{
+ SilcBuffer channels = NULL, channel_users = NULL;
+
+ SILC_LOG_DEBUG(("Announcing channels and channel users"));
+
+ /* Get channels and channel users in local list */
+ silc_server_announce_get_channels(server, server->local_list,
+ &channels, &channel_users);
+
+ /* Get channels and channel users in global list */
+ silc_server_announce_get_channels(server, server->global_list,
+ &channels, &channel_users);
+
+ if (channels) {
+ silc_buffer_push(channels, channels->data - channels->head);
+ SILC_LOG_HEXDUMP(("channels"), channels->data, channels->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_NEW_CHANNEL_LIST, 0,
+ channels->data, channels->len,
+ FALSE);
+
+ silc_buffer_free(channels);
+ }
+
+ if (channel_users) {
+ silc_buffer_push(channel_users, channel_users->data - channel_users->head);
+ SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
+ channel_users->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_NEW_CHANNEL_USER_LIST, 0,
+ channel_users->data, channel_users->len,
+ FALSE);
+
+ silc_buffer_free(channel_users);
+ }
+}