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;
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);
+
out:
/* Free the temporary connection data context */
if (sconn)
(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);
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;
FALSE : TRUE, user_data->id,
SILC_ID_CLIENT_LEN, SILC_ID_CLIENT);
+ /* 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);
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;
+ }
+
+ /* Remove client from channel's client list */
+ silc_list_del(channel->user_list, chl);
+ silc_free(chl);
+ server->stat.my_chanclients--;
- /* 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 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;
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);
+}