silc_server_disconnect_remote(server, server->sockets[i],
SILC_STATUS_OK,
"Server is shutting down");
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock,
- "Server is shutting down");
- silc_socket_free(sock);
+ if (server->sockets[i]) {
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock,
+ "Server is shutting down");
+ silc_socket_free(sock);
+ }
} else {
silc_socket_free(server->sockets[i]);
server->sockets[i] = NULL;
NULL : ptr->host, ptr->port,
SILC_SOCKET_TYPE_ROUTER)) {
SILC_LOG_DEBUG(("We are already connected to this router"));
- continue;
+
+ /* If we don't have primary router and this connection is our
+ primary router we are in desync. Reconnect to the primary. */
+ if (server->standalone && !server->router) {
+ SilcServerConfigRouter *primary =
+ silc_server_config_get_primary_router(server);
+ if (primary == ptr) {
+ SilcSocketConnection sock =
+ silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
+ ptr->host, ptr->port);
+ if (sock) {
+ server->backup_noswitch = TRUE;
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ silc_server_disconnect_remote(server, sock, 0, NULL);
+ server->backup_noswitch = FALSE;
+ SILC_LOG_DEBUG(("Reconnecting to primary router"));
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ } else {
+ continue;
+ }
}
if (silc_server_num_sockets_by_remote(server,
silc_net_is_ip(ptr->host) ?
server->router = id_entry;
server->router->server_type = SILC_ROUTER;
server->standalone = FALSE;
+ server->backup_primary = FALSE;
- /* If we are router then announce our possible servers. Backup
- router announces also global servers. */
- if (server->server_type == SILC_ROUTER)
- silc_server_announce_servers(server,
- server->backup_router ? TRUE : FALSE,
- 0, SILC_PRIMARY_ROUTE(server));
+ /* Announce data if we are not backup router (unless not as primary
+ currently). Backup router announces later at the end of
+ resuming protocol. */
+ if (server->backup_router && server->server_type == SILC_ROUTER) {
+ SILC_LOG_DEBUG(("Announce data after resume protocol"));
+ } else {
+ /* If we are router then announce our possible servers. Backup
+ router announces also global servers. */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_announce_servers(server,
+ server->backup_router ? TRUE : FALSE,
+ 0, SILC_PRIMARY_ROUTE(server));
- /* Announce our clients and channels to the router */
- silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
- silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
+ silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+ }
/* If we are backup router then this primary router is whom we are
backing up. */
server->schedule);
out:
- silc_protocol_free(protocol);
+ if (sock->protocol == protocol)
+ silc_protocol_free(protocol);
if (ctx->packet)
silc_packet_context_free(ctx->packet);
if (ctx->ske)
/* Do not send data to disconnected connection */
if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
SILC_LOG_DEBUG(("Disconnected socket connection, cannot send"));
+ SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
return;
}
SilcIDListData idata = (SilcIDListData)sock->user_data;
int ret;
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+ SILC_LOG_DEBUG(("Connection is disconnected"));
+ goto out;
+ }
+
server->stat.packets_received++;
/* Parse the packet */
silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
parser_context);
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+ SILC_LOG_DEBUG(("Connection is disconnected"));
+ return FALSE;
+ }
+
/* Reprocess data since we'll return FALSE here. This is because
the idata->receive_key might have become valid in the last packet
and we want to call this processor with valid cipher. */
silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
parser_context);
break;
- default:
- return TRUE;
}
return TRUE;
*/
if (packet->flags & SILC_PACKET_FLAG_LIST)
break;
- if (sock->protocol) {
- sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
- silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
- break;
- }
/* Check for failure START_USE from backup router */
if (server->server_type == SILC_SERVER &&
if (type == SILC_SERVER_BACKUP_START_USE) {
/* Attempt to reconnect to primary */
SILC_LOG_DEBUG(("Received failed START_USE from backup %s", sock->ip));
+
+ /* Default action is to disconnect from backup and reconnect to
+ primary. Since this failure can happen during switching to
+ backup (backup might have not noticed the primary going down yet),
+ we will wait a while and keep sending START_USE to backup.
+ Only after that we'll give up. */
+ if (server->router == sock->user_data &&
+ (time(0) - server->router_connect) < 30) {
+ SILC_LOG_DEBUG(("Resending START_USE to backup router"));
+ silc_server_backup_send_start_use(server, sock, FALSE);
+ break;
+ }
+
+ /* If backup is our primary, disconnect now. */
+ if (server->router == sock->user_data) {
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+ SILC_SET_DISCONNECTING(sock);
+ silc_server_close_connection(server, sock);
+ }
+
+ /* Reconnect */
silc_server_create_connections(server);
}
}
+
+ /* Execute protocol */
+ if (sock->protocol) {
+ sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE;
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+ break;
+ }
break;
case SILC_PACKET_REJECT:
silc_protocol_execute(sock->protocol, server->schedule, 0, 100000);
} else {
SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
- "protocol active, packet dropped."));
+ "protocol active (%s:%d [%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")));
}
break;
}
} else {
SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange "
- "protocol active, packet dropped."));
+ "protocol active (%s:%d [%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")));
}
break;
}
} else {
SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange "
- "protocol active, packet dropped."));
+ "protocol active (%s:%d [%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")));
}
break;
silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
} else {
SILC_LOG_ERROR(("Received Connection Auth packet but no authentication "
- "protocol active, packet dropped."));
+ "protocol active (%s:%d [%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")));
}
break;
silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
} else {
SILC_LOG_ERROR(("Received Re-key done packet but no re-key "
- "protocol active, packet dropped."));
+ "protocol active (%s:%d [%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")));
}
break;
"Router"), tmp[0] ? tmp : ""));
SILC_SET_DISCONNECTED(sock);
+ silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
silc_schedule_task_add(server->schedule, sock->sock,
silc_server_close_connection_final,
(void *)sock, 0, 1, SILC_TASK_TIMEOUT,
if (server->router == user_data) {
/* Check whether we have a backup router connection */
if (!backup_router || backup_router == user_data) {
- silc_server_create_connections(server);
+ if (!server->no_reconnect)
+ silc_server_create_connections(server);
server->id_entry->router = NULL;
server->router = NULL;
server->standalone = TRUE;
/* We stop here to take a breath */
sleep(2);
- if (server->server_type == SILC_BACKUP_ROUTER) {
+ if (server->backup_router) {
server->server_type = SILC_ROUTER;
/* We'll need to constantly try to reconnect to the primary
} else if (server->server_type == SILC_SERVER &&
sock->type == SILC_SOCKET_TYPE_ROUTER) {
/* Reconnect to the router (backup) */
- silc_server_create_connections(server);
+ if (!server->no_reconnect)
+ silc_server_create_connections(server);
}
if (user_data->server_name)
if (!client)
return;
- SILC_LOG_DEBUG(("Removing client from joined channels"));
-
if (notify && !client->id)
notify = FALSE;
+ SILC_LOG_DEBUG(("Removing client %s from joined channels",
+ notify ? silc_id_render(client->id, SILC_ID_CLIENT) : ""));
+
if (notify) {
clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
if (!clidp)
!silc_server_channel_has_local(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_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message ? 2 : 1,
clidp->data, clidp->len,
/* Send notify to channel about client leaving SILC and channel too */
if (notify)
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message ? 2 : 1,
clidp->data, clidp->len,
!silc_server_channel_has_local(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_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
SILC_NOTIFY_TYPE_LEAVE, 1,
clidp->data, clidp->len);
/* Send notify to channel about client leaving the channel */
if (notify)
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE,
SILC_NOTIFY_TYPE_LEAVE, 1,
clidp->data, clidp->len);
}
}
+/* Returns channel's invite and ban lists */
+
+void silc_server_announce_get_inviteban(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *invite,
+ SilcBuffer *ban)
+{
+ SilcBuffer list, idp, idp2, tmp2;
+ SilcUInt32 type;
+ SilcHashTableList htl;
+ const unsigned char a[1] = { 0x03 };
+
+ idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL);
+
+ /* Encode invite list */
+ if (channel->invite_list && silc_hash_table_count(channel->invite_list)) {
+ list = silc_buffer_alloc_size(2);
+ type = silc_hash_table_count(channel->invite_list);
+ SILC_PUT16_MSB(type, list->data);
+ silc_hash_table_list(channel->invite_list, &htl);
+ while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+ list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+ type);
+ silc_hash_table_list_reset(&htl);
+
+ idp2 = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+ *invite =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_INVITE, 5,
+ idp->data, idp->len,
+ channel->channel_name,
+ strlen(channel->channel_name),
+ idp2->data, idp2->len,
+ a, 1,
+ list->data, list->len);
+ silc_buffer_free(idp2);
+ silc_buffer_free(list);
+ }
+
+ /* Encode ban list */
+ if (channel->ban_list && silc_hash_table_count(channel->ban_list)) {
+ list = silc_buffer_alloc_size(2);
+ type = silc_hash_table_count(channel->ban_list);
+ SILC_PUT16_MSB(type, list->data);
+ silc_hash_table_list(channel->ban_list, &htl);
+ while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+ list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
+ type);
+ silc_hash_table_list_reset(&htl);
+
+ *ban =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_BAN, 3,
+ idp->data, idp->len,
+ a, 1,
+ list->data, list->len);
+ silc_buffer_free(list);
+ }
+
+ silc_buffer_free(idp);
+}
+
/* Returns assembled packets for channel users of the `channel'. */
void silc_server_announce_get_channel_users(SilcServer server,
SilcBuffer **channel_users_modes,
SilcUInt32 *channel_users_modes_c,
SilcBuffer **channel_topics,
+ SilcBuffer **channel_invites,
+ SilcBuffer **channel_bans,
SilcChannelID ***channel_ids,
unsigned long creation_time)
{
(*channel_topics)[i] = NULL;
silc_server_announce_get_channel_topic(server, channel,
&(*channel_topics)[i]);
- (*channel_users_modes_c)++;
+ /* Channel's invite and ban list */
+ *channel_invites = silc_realloc(*channel_invites,
+ sizeof(**channel_invites) * (i + 1));
+ (*channel_invites)[i] = NULL;
+ *channel_bans = silc_realloc(*channel_bans,
+ sizeof(**channel_bans) * (i + 1));
+ (*channel_bans)[i] = NULL;
+ silc_server_announce_get_inviteban(server, channel,
+ &(*channel_invites)[i],
+ &(*channel_bans)[i]);
+
+ (*channel_users_modes_c)++;
silc_free(cid);
i++;
SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
SilcBuffer *channel_users_modes = NULL;
SilcBuffer *channel_topics = NULL;
+ SilcBuffer *channel_invites = NULL;
+ SilcBuffer *channel_bans = NULL;
SilcUInt32 channel_users_modes_c = 0;
SilcChannelID **channel_ids = NULL;
&channel_users_modes,
&channel_users_modes_c,
&channel_topics,
+ &channel_invites,
+ &channel_bans,
&channel_ids, creation_time);
/* Get channels and channel users in global list */
&channel_users_modes,
&channel_users_modes_c,
&channel_topics,
+ &channel_invites,
+ &channel_bans,
&channel_ids, creation_time);
if (channels) {
silc_free(channel_topics);
}
+ if (channel_invites) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_invites[i])
+ continue;
+
+ silc_buffer_push(channel_invites[i],
+ channel_invites[i]->data -
+ channel_invites[i]->head);
+ SILC_LOG_HEXDUMP(("channel invite list"), channel_invites[i]->data,
+ channel_invites[i]->len);
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_invites[i]->data,
+ channel_invites[i]->len,
+ FALSE);
+ silc_buffer_free(channel_invites[i]);
+ }
+ silc_free(channel_invites);
+ }
+
+ if (channel_bans) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_bans[i])
+ continue;
+
+ silc_buffer_push(channel_bans[i],
+ channel_bans[i]->data -
+ channel_bans[i]->head);
+ SILC_LOG_HEXDUMP(("channel ban list"), channel_bans[i]->data,
+ channel_bans[i]->len);
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_bans[i]->data,
+ channel_bans[i]->len,
+ FALSE);
+ silc_buffer_free(channel_bans[i]);
+ }
+ silc_free(channel_bans);
+ }
+
silc_free(channel_ids);
}
return buffer;
}
+/* Timeout callback for unsuccessful rekey. The rekey did not go through
+ for some reason. */
+
+SILC_TASK_CALLBACK(silc_server_rekey_timeout)
+{
+ SilcServerRekeyInternalContext *ctx = context;
+ SilcServer server = app_context;
+ SilcSocketConnection sock = ctx->sock;
+
+ SILC_LOG_DEBUG(("Timeout occurred in rekey protocol with %s:%d [%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")));
+
+ SILC_LOG_WARNING(("Timeout occurred in rekey protocol with %s:%d [%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")));
+
+ if (sock->protocol) {
+ silc_protocol_cancel(sock->protocol, server->schedule);
+ silc_protocol_free(sock->protocol);
+ sock->protocol = NULL;
+ }
+ if (ctx->packet)
+ silc_packet_context_free(ctx->packet);
+ if (ctx->ske)
+ silc_ske_free(ctx->ske);
+ silc_socket_free(sock);
+ silc_free(ctx);
+
+ /* Disconnect since we failed to rekey, the keys are probably wrong. */
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+
+ /* Reconnect */
+ if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+ silc_server_create_connections(server);
+}
+
/* A timeout callback for the re-key. We will be the initiator of the
re-key protocol. */
SilcProtocol protocol;
SilcServerRekeyInternalContext *proto_ctx;
+ if (!idata)
+ return;
+
+ /* Do not execute rekey with disabled connections, as it would not
+ go through anyway. */
+ if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+ return;
+
/* If rekey protocol is active already wait for it to finish */
if (sock->protocol && sock->protocol->protocol &&
sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)
return;
+ /* If any other protocol is active do not start this protocol yet. */
+ if (sock->protocol) {
+ SILC_LOG_DEBUG(("Waiting for other protocol to finish before rekeying"));
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rekey_callback,
+ sock, 60, 0, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%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")));
+
/* Allocate internal protocol context. This is sent as context
to the protocol. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
proto_ctx->server = (void *)server;
- proto_ctx->sock = sock;
+ proto_ctx->sock = silc_socket_dup(sock);
proto_ctx->responder = FALSE;
proto_ctx->pfs = idata->rekey->pfs;
&protocol, proto_ctx, silc_server_rekey_final);
sock->protocol = protocol;
+ /* Register timeout callback in case the rekey does not go through. */
+ proto_ctx->timeout_task =
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_rekey_timeout,
+ proto_ctx,
+ (idata->rekey->timeout >
+ server->config->key_exchange_timeout ?
+ idata->rekey->timeout :
+ server->config->key_exchange_timeout * 4), 0,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
/* Run the protocol */
silc_protocol_execute(protocol, server->schedule, 0, 0);
}
SilcSocketConnection sock = ctx->sock;
SilcIDListData idata = (SilcIDListData)sock->user_data;
+ if (ctx->timeout_task)
+ silc_schedule_task_del(server->schedule, ctx->timeout_task);
+
if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
+ silc_socket_free(sock);
silc_free(ctx);
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
- SILC_LOG_DEBUG(("Rekey protocol completed"));
+ SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%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")));
/* Purge the outgoing data queue to assure that all rekey packets really
go to the network before we quit the protocol. */
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
+ silc_socket_free(sock);
silc_free(ctx);
}