Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
- Copyright (C) 1997 - 2000 Pekka Riikonen
+ Copyright (C) 1997 - 2001 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
if (server->pending_commands)
silc_dlist_uninit(server->pending_commands);
- silc_math_primegen_uninit(); /* XXX */
silc_free(server);
}
}
/* Set log files where log message should be saved. */
server->config->server = server;
- silc_config_server_setlogfiles(server->config);
+ silc_server_config_setlogfiles(server->config);
/* Register all configured ciphers, PKCS and hash functions. */
- silc_config_server_register_ciphers(server->config);
- silc_config_server_register_pkcs(server->config);
- silc_config_server_register_hashfuncs(server->config);
+ silc_server_config_register_ciphers(server->config);
+ silc_server_config_register_pkcs(server->config);
+ silc_server_config_register_hashfuncs(server->config);
/* 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);
return FALSE;
}
+/* Fork server to background and set gid+uid to non-root.
+ Silcd will not run as root, so trying to set either user or group to
+ root will cause silcd to exit. */
+
+void silc_server_daemonise(SilcServer server)
+{
+ /* 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);
+
+ 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) {
+ 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 */
+ if (fork()) {
+ SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+ SILC_LOG_DEBUG(("Forking SILC server to background"));
+ exit(0);
+ }
+ setsid();
+
+ SILC_LOG_DEBUG(("Changing to group %s", group));
+ if(setgid(gr->gr_gid)==0) {
+ SILC_LOG_DEBUG(("Setgid to %s", group));
+ } else {
+ SILC_LOG_DEBUG(("Setgid to %s failed", group));
+ fprintf(stderr, "Tried to setgid %s but no such group. Exiting\n",
+ group);
+ exit(1);
+ }
+ SILC_LOG_DEBUG(("Changing to user nobody"));
+ if(setuid(pw->pw_uid)==0) {
+ SILC_LOG_DEBUG(("Setuid to %s", user));
+ } else {
+ SILC_LOG_DEBUG(("Setuid to %s failed", user));
+ fprintf(stderr, "Tried to setuid %s but no such user. Exiting\n",
+ user);
+ exit(1);
+ }
+ }
+ } else {
+ /* Fork server to background, making it a daemon */
+ if (fork()) {
+ SILC_LOG_DEBUG(("Server started as user"));
+ SILC_LOG_DEBUG(("Forking SILC server to background"));
+ exit(0);
+ }
+ setsid();
+ }
+}
+
/* 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. */
protocol. */
silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
server->sockets[sock] = newsocket;
- newsocket->hostname = sconn->remote_host;
+ newsocket->hostname = strdup(sconn->remote_host);
+ newsocket->ip = strdup(sconn->remote_host);
newsocket->port = sconn->remote_port;
sconn->sock = newsocket;
/* If we are a SILC router we need to establish all of our primary
routes. */
if (server->server_type == SILC_ROUTER) {
- SilcConfigServerSectionServerConnection *ptr;
+ SilcServerConfigSectionServerConnection *ptr;
SILC_LOG_DEBUG(("We are router"));
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;
proto_ctx->dest_id = ctx->dest_id;
/* Resolve the authentication method used in this connection */
- proto_ctx->auth_meth = SILC_PROTOCOL_CONN_AUTH_PASSWORD;
+ proto_ctx->auth_meth = SILC_AUTH_PASSWORD;
if (server->config->routers) {
- SilcConfigServerSectionServerConnection *conn = NULL;
+ SilcServerConfigSectionServerConnection *conn = NULL;
/* Check if we find a match from user configured connections */
- conn = silc_config_server_find_router_conn(server->config,
+ conn = silc_server_config_find_router_conn(server->config,
sock->hostname,
sock->port);
if (conn) {
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"));
break;
}
+ /* Statistics */
server->stat.my_clients++;
+ server->stat.clients++;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_clients++;
id_entry = (void *)client;
break;
break;
}
+ /* Statistics */
if (sock->type == SILC_SOCKET_TYPE_SERVER)
server->stat.my_servers++;
else
server->stat.my_routers++;
+ server->stat.servers++;
id_entry = (void *)new_server;
/* 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)
it later. */
if (ret == -2)
return;
+
+ if (ret == -1)
+ return;
/* The packet has been sent and now it is time to set the connection
back to only for input. When there is again some outgoing data
}
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 (!server->standalone && server->server_type == SILC_SERVER &&
+ sock == server->router->connection)
+ silc_task_register(server->timeout_queue, 0,
+ silc_server_connect_to_router,
+ context, 0, 500000,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
if (sock->user_data)
silc_server_free_sock_user_data(server, sock);
/* 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_ID_SERVER_COMPARE(packet->dst_id, server->id_string)) {
/* Route the packet to fastest route for the destination ID */
- void *id = silc_id_str2id(packet->dst_id, packet->dst_id_type);
+ void *id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ packet->dst_id_type);
+ if (!id)
+ goto out;
silc_server_packet_route(server,
silc_server_route_get(server, id,
packet->dst_id_type),
switch(type) {
case SILC_PACKET_DISCONNECT:
SILC_LOG_DEBUG(("Disconnect packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
break;
case SILC_PACKET_SUCCESS:
* success message is for whatever protocol is executing currently.
*/
SILC_LOG_DEBUG(("Success packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
if (sock->protocol) {
sock->protocol->execute(server->timeout_queue, 0,
sock->protocol, sock->sock, 0, 0);
* failure message is for whatever protocol is executing currently.
*/
SILC_LOG_DEBUG(("Failure packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
if (sock->protocol) {
/* XXX Audit the failure type */
sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
case SILC_PACKET_REJECT:
SILC_LOG_DEBUG(("Reject packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
return;
break;
* router. Server then relays the notify messages to clients if needed.
*/
SILC_LOG_DEBUG(("Notify packet"));
- silc_server_notify(server, sock, packet);
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_notify_list(server, sock, packet);
+ else
+ silc_server_notify(server, sock, packet);
break;
/*
* specially.
*/
SILC_LOG_DEBUG(("Channel Message packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_channel_message(server, sock, packet);
break;
* never receives this channel and thus is ignored.
*/
SILC_LOG_DEBUG(("Channel Key packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_channel_key(server, sock, packet);
break;
* command context and calls the command.
*/
SILC_LOG_DEBUG(("Command packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_command_process(server, sock, packet);
break;
* that we've routed further.
*/
SILC_LOG_DEBUG(("Command Reply packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_command_reply(server, sock, packet);
break;
* client or server.
*/
SILC_LOG_DEBUG(("Private Message packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_private_message(server, sock, packet);
break;
/*
* Private message key packet.
*/
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
break;
/*
*/
case SILC_PACKET_KEY_EXCHANGE:
SILC_LOG_DEBUG(("KE packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
case SILC_PACKET_KEY_EXCHANGE_1:
SILC_LOG_DEBUG(("KE 1 packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
proto_ctx->packet = silc_packet_context_dup(packet);
proto_ctx->dest_id_type = packet->src_id_type;
- proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+ proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!proto_ctx->dest_id)
+ break;
/* Let the protocol handle the packet */
sock->protocol->execute(server->timeout_queue, 0,
case SILC_PACKET_KEY_EXCHANGE_2:
SILC_LOG_DEBUG(("KE 2 packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_KEY_EXCHANGE) {
proto_ctx->packet = silc_packet_context_dup(packet);
proto_ctx->dest_id_type = packet->src_id_type;
- proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_type);
+ proto_ctx->dest_id = silc_id_str2id(packet->src_id, packet->src_id_len,
+ packet->src_id_type);
+ if (!proto_ctx->dest_id)
+ break;
/* Let the protocol handle the packet */
sock->protocol->execute(server->timeout_queue, 0,
* at any time.
*/
SILC_LOG_DEBUG(("Connection authentication request packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
break;
/*
/* Start of the authentication protocol. We receive here the
authentication data and will verify it. */
SILC_LOG_DEBUG(("Connection auth packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+
if (sock->protocol && sock->protocol->protocol->type
== SILC_PROTOCOL_SERVER_CONNECTION_AUTH) {
* SILC network.
*/
SILC_LOG_DEBUG(("New ID packet"));
- silc_server_new_id(server, sock, packet);
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_new_id_list(server, sock, packet);
+ else
+ silc_server_new_id(server, sock, packet);
break;
case SILC_PACKET_NEW_CLIENT:
* ID we will send it to the client.
*/
SILC_LOG_DEBUG(("New Client packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_new_client(server, sock, packet);
break;
* connected to us.
*/
SILC_LOG_DEBUG(("New Server packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
silc_server_new_server(server, sock, packet);
break;
* network are distributed using this packet.
*/
SILC_LOG_DEBUG(("New Channel packet"));
- silc_server_new_channel(server, sock, packet);
- break;
-
- case SILC_PACKET_NEW_CHANNEL_USER:
- /*
- * Received new channel user packet. Information about new users on a
- * channel are distributed between routers using this packet. The
- * router receiving this will redistribute it and also sent JOIN notify
- * to local clients on the same channel. Normal server sends JOIN notify
- * to its local clients on the channel.
- */
- SILC_LOG_DEBUG(("New Channel User packet"));
- silc_server_new_channel_user(server, sock, packet);
- break;
-
- case SILC_PACKET_NEW_CHANNEL_LIST:
- /*
- * List of new channel packets received. This is usually received when
- * existing server or router connects to us and distributes information
- * of all channels it has.
- */
- break;
-
- case SILC_PACKET_NEW_CHANNEL_USER_LIST:
- /*
- * List of new channel user packets received. This is usually received
- * when existing server or router connects to us and distributes
- * information of all channel users it has.
- */
- break;
-
- case SILC_PACKET_REPLACE_ID:
- /*
- * Received replace ID packet. This sends the old ID that is to be
- * replaced with the new one included into the packet. Client must not
- * send this packet.
- */
- SILC_LOG_DEBUG(("Replace ID packet"));
- silc_server_replace_id(server, sock, packet);
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ silc_server_new_channel_list(server, sock, packet);
+ else
+ silc_server_new_channel(server, sock, packet);
break;
- case SILC_PACKET_REMOVE_ID:
+ case SILC_PACKET_HEARTBEAT:
/*
- * Received remove ID Packet.
+ * Received heartbeat.
*/
- SILC_LOG_DEBUG(("Remove ID packet"));
- silc_server_remove_id(server, sock, packet);
- break;
-
- case SILC_PACKET_REMOVE_CHANNEL_USER:
- /*
- * Received packet to remove user from a channel. Routers notify other
- * routers about a user leaving a channel.
- */
- SILC_LOG_DEBUG(("Remove Channel User packet"));
- silc_server_remove_channel_user(server, sock, packet);
+ SILC_LOG_DEBUG(("Heartbeat packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
break;
default:
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_close_connection(server, sock);
}
-/* Free's user_data pointer from socket connection object. This also sends
+/* Frees client data and notifies about client's signoff. */
+
+void silc_server_free_client_data(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientEntry user_data, char *signoff)
+{
+ /* Send REMOVE_ID packet to routers. */
+ if (!server->standalone && server->router)
+ silc_server_send_notify_signoff(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, user_data->id,
+ SILC_ID_CLIENT_LEN, signoff);
+
+ /* Remove client from all channels */
+ silc_server_remove_from_channels(server, sock, user_data, signoff);
+
+ /* XXX must take some info to history before freeing */
+
+ /* Free the client entry and everything in it */
+ silc_idlist_del_data(user_data);
+ silc_idlist_del_client(server->local_list, user_data);
+ server->stat.my_clients--;
+ server->stat.clients--;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_clients--;
+}
+
+/* Frees user_data pointer from socket connection object. This also sends
appropriate notify packets to the network to inform about leaving
entities. */
case SILC_SOCKET_TYPE_CLIENT:
{
SilcClientEntry user_data = (SilcClientEntry)sock->user_data;
-
- /* Remove client from all channels */
- silc_server_remove_from_channels(server, sock, user_data);
-
- /* XXX must take some info to history before freeing */
-
- /* Send REMOVE_ID packet to routers. */
- if (!server->standalone && server->router)
- 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);
-
- /* 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--;
+ silc_server_free_client_data(server, sock, user_data, NULL);
break;
}
case SILC_SOCKET_TYPE_SERVER:
/* Send REMOVE_ID packet to routers. */
if (!server->standalone && server->router)
- 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_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);
+
+ /* 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);
silc_idlist_del_server(server->local_list, user_data);
server->stat.my_servers--;
+ server->stat.servers--;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_servers--;
break;
}
default:
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, NULL);
+ 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, NULL);
+ 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. */
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
- SilcClientEntry client)
+ SilcClientEntry client,
+ char *signoff_message)
{
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) {
-
- /* 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. */
- 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);
+ /* 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;
}
- /* Remove from list */
+ /* Remove client from channel's client list */
silc_list_del(channel->user_list, chl);
silc_free(chl);
server->stat.my_chanclients--;
+ /* If there is no global users on the channel anymore mark the channel
+ as local channel. */
+ if (server->server_type == SILC_SERVER &&
+ !silc_server_channel_has_global(channel))
+ channel->global_users = FALSE;
+
+ /* 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,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
+
+ if (!silc_idlist_del_channel(server->local_list, channel))
+ silc_idlist_del_channel(server->global_list, channel);
+ 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, 1,
- clidp->data, clidp->len);
- silc_buffer_free(chidp);
+ SILC_NOTIFY_TYPE_SIGNOFF,
+ signoff_message ? 2 : 1,
+ clidp->data, clidp->len,
+ signoff_message, signoff_message ?
+ strlen(signoff_message) : 0);
}
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;
SILC_TASK_CALLBACK(silc_server_timeout_remote)
{
- SilcServerConnection sconn = (SilcServerConnection)context;
- SilcSocketConnection sock = sconn->server->sockets[fd];
+ SilcServer server = (SilcServer)context;
+ SilcSocketConnection sock = server->sockets[fd];
if (!sock)
return;
- silc_server_disconnect_remote(sconn->server, sock,
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock);
+
+ silc_server_disconnect_remote(server, sock,
"Server closed connection: "
"Connection timeout");
}
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);
}
/* Get channel ID */
tmp = silc_channel_key_get_id(payload, &tmp_len);
- id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
+ id = silc_id_str2id(tmp, tmp_len, SILC_ID_CHANNEL);
if (!id) {
channel = NULL;
goto out;
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, SILC_PACKET_FLAG_LIST,
+ 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, SILC_PACKET_FLAG_LIST,
+ clients->data, clients->len, TRUE);
+
+ silc_buffer_free(clients);
+ }
+}
+
+static SilcBuffer
+silc_server_announce_encode_join(unsigned int argc, ...)
+{
+ va_list ap;
+
+ va_start(ap, argc);
+ return silc_notify_payload_encode(SILC_NOTIFY_TYPE_JOIN, argc, ap);
+}
+
+/* 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;
+ SilcBuffer chidp;
+ 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 */
+ 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_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, SILC_PACKET_FLAG_LIST,
+ 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_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_users->data, channel_users->len,
+ FALSE);
+
+ silc_buffer_free(channel_users);
+ }
+}