silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
silc_ske_free_key_material(ctx->keymat);
silc_free(sconn);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
return;
}
protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
/* Error occured during protocol */
silc_free(ctx->dest_id);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
+ NULL);
goto out;
}
if (!id_entry) {
silc_free(ctx->dest_id);
SILC_LOG_ERROR(("Cannot add new server entry to cache"));
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
+ NULL);
goto out;
}
sock->ip ? sock->ip : ""));
server->stat.conn_failures++;
silc_server_disconnect_remote(server, sock,
- "Server closed connection: Unknown host");
+ SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
+ "Unknown host or IP");
return;
}
/* The connection is denied */
SILC_LOG_INFO(("Connection %s (%s) is denied",
sock->hostname, sock->ip));
- silc_server_disconnect_remote(server, sock, deny->reason ?
- deny->reason :
- "Server closed connection: "
- "Connection refused");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER,
+ deny->reason);
server->stat.conn_failures++;
return;
}
SILC_LOG_INFO(("Connection %s (%s) is not allowed", sock->hostname,
sock->ip));
silc_server_disconnect_remote(server, sock,
- "Server closed connection: "
- "Connection refused");
+ SILC_STATUS_ERR_BANNED_FROM_SERVER);
server->stat.conn_failures++;
return;
}
silc_free(ctx);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
+ NULL);
server->stat.auth_failures++;
return;
}
silc_free(ctx);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Key exchange failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
server->stat.auth_failures++;
return;
}
silc_free(ctx);
silc_schedule_task_del_by_callback(server->schedule,
silc_server_failure_callback);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED,
+ NULL);
server->stat.auth_failures++;
return;
}
if (!client) {
SILC_LOG_ERROR(("Could not add new client to cache"));
silc_free(sock->user_data);
- silc_server_disconnect_remote(server, sock,
- "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
server->stat.auth_failures++;
goto out;
}
if (!new_server) {
SILC_LOG_ERROR(("Could not add new server to cache"));
silc_free(sock->user_data);
- silc_server_disconnect_remote(server, sock,
- "Server closed connection: "
- "Authentication failed");
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
server->stat.auth_failures++;
goto out;
}
/* Parse the packet type */
switch (type) {
case SILC_PACKET_DISCONNECT:
- SILC_LOG_DEBUG(("Disconnect packet"));
- if (packet->flags & SILC_PACKET_FLAG_LIST)
- break;
- if (silc_string_is_ascii(packet->buffer->data, packet->buffer->len)) {
- /* Duplicate to null terminate the string. */
- char *message = silc_memdup(packet->buffer->data, packet->buffer->len);
- SILC_LOG_ERROR(("%s", message));
+ {
+ SilcStatus status;
+ char *message = NULL;
+
+ SILC_LOG_DEBUG(("Disconnect packet"));
+
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ if (packet->buffer->len < 1)
+ break;
+
+ status = (SilcStatus)packet->buffer->data[0];
+ if (packet->buffer->len > 1 &&
+ silc_utf8_valid(packet->buffer->data + 1, packet->buffer->len - 1))
+ message = silc_memdup(packet->buffer->data, packet->buffer->len);
+
+ SILC_LOG_ERROR(("Disconnected by %s (%s): %s (%d) %s",
+ sock->ip, sock->hostname,
+ silc_get_status_message(status), status,
+ message ? message : ""));
silc_free(message);
}
break;
void silc_server_disconnect_remote(SilcServer server,
SilcSocketConnection sock,
- const char *fmt, ...)
+ SilcStatus status, ...)
{
va_list ap;
- unsigned char buf[4096];
+ unsigned char buf[512];
+ SilcBuffer buffer;
+ char *cp;
+ int len;
if (!sock)
return;
memset(buf, 0, sizeof(buf));
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
+ va_start(ap, status);
+ cp = va_arg(ap, char *);
+ if (cp) {
+ vsnprintf(buf, sizeof(buf) - 1, cp, ap);
+ cp = buf;
+ }
va_end(ap);
SILC_LOG_DEBUG(("Disconnecting remote host"));
/* Notify remote end that the conversation is over. The notify message
is tried to be sent immediately. */
+
+ len = 1;
+ if (cp)
+ len += silc_utf8_encoded_len(buf, strlen(buf), SILC_STRING_ASCII);
+
+ buffer = silc_buffer_alloc_size(len);
+ if (!buffer)
+ goto out;
+
+ buffer->data[0] = status;
+ if (cp)
+ silc_utf8_encode(buf, strlen(buf), SILC_STRING_ASCII, buffer->data + 1,
+ buffer->len - 1);
silc_server_packet_send(server, sock, SILC_PACKET_DISCONNECT, 0,
- buf, strlen(buf), TRUE);
+ buffer->data, buffer->len, TRUE);
+ silc_buffer_free(buffer);
+
+ out:
silc_server_packet_queue_purge(server, sock);
/* Mark the connection to be disconnected */
to the network before removing the client entry. */
silc_server_packet_queue_purge(server, sock);
- if (!client->id)
- return;
-
- /* Send SIGNOFF notify to routers. */
- if (notify && !server->standalone && server->router)
- silc_server_send_notify_signoff(server, server->router->connection,
- server->server_type == SILC_SERVER ?
- FALSE : TRUE, client->id, signoff);
+ if (client->id) {
+ /* Send SIGNOFF notify to routers. */
+ if (notify && !server->standalone && server->router)
+ silc_server_send_notify_signoff(server, server->router->connection,
+ server->server_type == SILC_SERVER ?
+ FALSE : TRUE, client->id, signoff);
- /* Remove client from all channels */
- if (notify)
- silc_server_remove_from_channels(server, NULL, client,
- TRUE, (char *)signoff, TRUE);
- else
- silc_server_remove_from_channels(server, NULL, client,
- FALSE, NULL, FALSE);
+ /* Remove client from all channels */
+ if (notify)
+ silc_server_remove_from_channels(server, NULL, client,
+ TRUE, (char *)signoff, TRUE);
+ else
+ silc_server_remove_from_channels(server, NULL, client,
+ FALSE, NULL, FALSE);
- /* Check if anyone is watching this nickname */
- if (server->server_type == SILC_ROUTER)
- silc_server_check_watcher_list(server, client, NULL,
- SILC_NOTIFY_TYPE_SIGNOFF);
+ /* Check if anyone is watching this nickname */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_check_watcher_list(server, client, NULL,
+ SILC_NOTIFY_TYPE_SIGNOFF);
- /* Remove this client from watcher list if it is */
- silc_server_del_from_watcher_list(server, client);
+ /* Remove this client from watcher list if it is */
+ silc_server_del_from_watcher_list(server, client);
+ }
/* Update statistics */
server->stat.my_clients--;
void silc_server_remove_from_channels(SilcServer server,
SilcSocketConnection sock,
SilcClientEntry client,
- int notify,
- char *signoff_message,
- int keygen)
+ bool notify,
+ const char *signoff_message,
+ bool keygen)
{
SilcChannelEntry channel;
SilcChannelClientEntry chl;
return;
clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (!clidp)
+ notify = FALSE;
/* Remove the client from all channels. The client is removed from
the channels' user list. */
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
- /* Remove channel from client's channel list */
- silc_hash_table_del(client->channels, channel);
-
- /* Remove channel if there is no users anymore */
+ /* Remove channel if this is last client leaving the channel, unless
+ the channel is permanent. */
if (server->server_type == SILC_ROUTER &&
silc_hash_table_count(channel->user_list) < 2) {
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
+ silc_server_channel_delete(server, channel);
continue;
}
- /* Remove client from channel's client list */
+ silc_hash_table_del(client->channels, channel);
silc_hash_table_del(channel->user_list, chl->client);
channel->user_count--;
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. */
+ need the channel entry anymore, we can remove it safely, unless the
+ channel is permanent channel */
if (server->server_type != SILC_ROUTER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
signoff_message, signoff_message ?
strlen(signoff_message) : 0);
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
- if (channel->founder_key) {
- /* The founder auth data exists, do not remove the channel entry */
- SilcChannelClientEntry chl2;
- SilcHashTableList htl2;
-
- channel->disabled = TRUE;
-
- silc_hash_table_list(channel->user_list, &htl2);
- while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
- silc_hash_table_del(chl2->client->channels, channel);
- silc_hash_table_del(channel->user_list, chl2->client);
- channel->user_count--;
- silc_free(chl2);
- }
- silc_hash_table_list_reset(&htl2);
- continue;
- }
-
- /* Remove the channel entry */
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ silc_server_channel_delete(server, channel);
continue;
}
- /* Send notify to channel about client leaving SILC and thus
- the entire channel. */
+ /* Send notify to channel about client leaving SILC and channel too */
if (notify)
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_SIGNOFF,
signoff_message, signoff_message ?
strlen(signoff_message) : 0);
+ /* Re-generate channel key if needed */
if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
- /* Re-generate channel key */
if (!silc_server_create_channel_key(server, channel, 0))
- goto out;
+ continue;
/* Send the channel key to the channel. The key of course is not sent
to the client who was removed from the channel. */
}
}
- out:
silc_hash_table_list_reset(&htl);
silc_buffer_free(clidp);
}
last client leaves the channel. If `notify' is FALSE notify messages
are not sent. */
-int silc_server_remove_from_one_channel(SilcServer server,
- SilcSocketConnection sock,
- SilcChannelEntry channel,
- SilcClientEntry client,
- int notify)
+bool silc_server_remove_from_one_channel(SilcServer server,
+ SilcSocketConnection sock,
+ SilcChannelEntry channel,
+ SilcClientEntry client,
+ bool notify)
{
SilcChannelClientEntry chl;
SilcBuffer clidp;
- SILC_LOG_DEBUG(("Start"));
+ SILC_LOG_DEBUG(("Removing %s from channel %s",
+ silc_id_render(client->id, SILC_ID_CLIENT),
+ channel->channel_name));
/* Get the entry to the channel, if this client is not on the channel
then return Ok. */
if (!silc_hash_table_find(client->channels, channel, NULL, (void *)&chl))
return TRUE;
- /* Remove the client from the channel. The client is removed from
- the channel's user list. */
-
- clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
-
- /* Remove channel from client's channel list */
- silc_hash_table_del(client->channels, chl->channel);
-
- /* Remove channel if there is no users anymore */
+ /* Remove channel if this is last client leaving the channel, unless
+ the channel is permanent. */
if (server->server_type == SILC_ROUTER &&
silc_hash_table_count(channel->user_list) < 2) {
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
- silc_buffer_free(clidp);
+ silc_server_channel_delete(server, channel);
return FALSE;
}
- /* Remove client from channel's client list */
+ silc_hash_table_del(client->channels, chl->channel);
silc_hash_table_del(channel->user_list, chl->client);
channel->user_count--;
silc_free(chl);
server->stat.my_chanclients--;
+ clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ if (!clidp)
+ notify = 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. */
+ need the channel entry anymore, we can remove it safely, unless the
+ channel is permanent channel */
if (server->server_type != SILC_ROUTER &&
!silc_server_channel_has_local(channel)) {
/* Notify about leaving client if this channel has global users. */
SILC_NOTIFY_TYPE_LEAVE, 1,
clidp->data, clidp->len);
+ silc_schedule_task_del_by_context(server->schedule, channel->rekey);
+ silc_server_channel_delete(server, channel);
silc_buffer_free(clidp);
-
- if (channel->rekey)
- silc_schedule_task_del_by_context(server->schedule, channel->rekey);
-
- if (channel->founder_key) {
- /* The founder auth data exists, do not remove the channel entry */
- SilcChannelClientEntry chl2;
- SilcHashTableList htl2;
-
- channel->disabled = TRUE;
-
- silc_hash_table_list(channel->user_list, &htl2);
- while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
- silc_hash_table_del(chl2->client->channels, channel);
- silc_hash_table_del(channel->user_list, chl2->client);
- channel->user_count--;
- silc_free(chl2);
- }
- silc_hash_table_list_reset(&htl2);
- return FALSE;
- }
-
- /* Remove the channel entry */
- if (silc_idlist_del_channel(server->local_list, channel))
- server->stat.my_channels--;
- else
- silc_idlist_del_channel(server->global_list, channel);
return FALSE;
}
{
SilcServer server = (SilcServer)context;
SilcSocketConnection sock = server->sockets[fd];
+ SilcProtocolType protocol = 0;
SILC_LOG_DEBUG(("Start"));
/* If we have protocol active we must assure that we call the protocol's
final callback so that all the memory is freed. */
if (sock->protocol) {
+ protocol = sock->protocol->protocol->type;
silc_protocol_cancel(sock->protocol, server->schedule);
sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
silc_protocol_execute_final(sock->protocol, server->schedule);
if (sock->user_data)
silc_server_free_sock_user_data(server, sock, NULL);
- silc_server_disconnect_remote(server, sock, "Server closed connection: "
+ silc_server_disconnect_remote(server, sock,
+ protocol ==
+ SILC_PROTOCOL_SERVER_CONNECTION_AUTH ?
+ SILC_STATUS_ERR_AUTH_FAILED :
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED,
"Connection timeout");
}
void silc_server_announce_get_channel_users(SilcServer server,
SilcChannelEntry channel,
+ SilcBuffer *channel_modes,
SilcBuffer *channel_users,
SilcBuffer *channel_users_modes)
{
SilcBuffer chidp, clidp;
SilcBuffer tmp;
int len;
- unsigned char mode[4];
+ unsigned char mode[4], *fkey = NULL;
+ SilcUInt32 fkey_len = 0;
+ char *hmac;
SILC_LOG_DEBUG(("Start"));
- /* Now find all users on the channel */
chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+
+ /* CMODE notify */
+ clidp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+ SILC_PUT32_MSB(channel->mode, mode);
+ hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
+ if (channel->founder_key)
+ fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
+ tmp =
+ silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
+ 6, clidp->data, clidp->len,
+ mode, sizeof(mode),
+ NULL, 0,
+ hmac, hmac ? strlen(hmac) : 0,
+ channel->passphrase,
+ channel->passphrase ?
+ strlen(channel->passphrase) : 0,
+ fkey, fkey_len);
+ len = tmp->len;
+ *channel_modes =
+ silc_buffer_realloc(*channel_modes,
+ (*channel_modes ?
+ (*channel_modes)->truelen + len : len));
+ silc_buffer_pull_tail(*channel_modes,
+ ((*channel_modes)->end -
+ (*channel_modes)->data));
+ silc_buffer_put(*channel_modes, tmp->data, tmp->len);
+ silc_buffer_pull(*channel_modes, len);
+ silc_buffer_free(tmp);
+ silc_buffer_free(clidp);
+ silc_free(fkey);
+
+ /* Now find all users on the channel */
silc_hash_table_list(channel->user_list, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
/* CUMODE notify for mode change on the channel */
SILC_PUT32_MSB(chl->mode, mode);
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && channel->founder_key)
+ fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
- 3, clidp->data, clidp->len,
+ 4, clidp->data, clidp->len,
mode, 4,
- clidp->data, clidp->len);
+ clidp->data, clidp->len,
+ fkey, fkey_len);
len = tmp->len;
*channel_users_modes =
silc_buffer_realloc(*channel_users_modes,
silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
silc_buffer_pull(*channel_users_modes, len);
silc_buffer_free(tmp);
-
+ silc_free(fkey);
silc_buffer_free(clidp);
}
silc_hash_table_list_reset(&htl);
void silc_server_announce_get_channels(SilcServer server,
SilcIDList id_list,
SilcBuffer *channels,
+ SilcBuffer **channel_modes,
SilcBuffer *channel_users,
SilcBuffer **channel_users_modes,
SilcUInt32 *channel_users_modes_c,
sizeof(**channel_users_modes) *
(i + 1));
(*channel_users_modes)[i] = NULL;
+ *channel_modes = silc_realloc(*channel_modes,
+ sizeof(**channel_modes) * (i + 1));
+ (*channel_modes)[i] = NULL;
*channel_ids = silc_realloc(*channel_ids,
sizeof(**channel_ids) * (i + 1));
(*channel_ids)[i] = NULL;
silc_server_announce_get_channel_users(server, channel,
+ &(*channel_modes)[i],
channel_users,
&(*channel_users_modes)[i]);
(*channel_ids)[i] = channel->id;
unsigned long creation_time,
SilcSocketConnection remote)
{
- SilcBuffer channels = NULL, channel_users = NULL;
+ SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
SilcBuffer *channel_users_modes = NULL;
SilcBuffer *channel_topics = NULL;
SilcUInt32 channel_users_modes_c = 0;
/* Get channels and channel users in local list */
silc_server_announce_get_channels(server, server->local_list,
- &channels, &channel_users,
+ &channels, &channel_modes,
+ &channel_users,
&channel_users_modes,
&channel_users_modes_c,
&channel_topics,
/* Get channels and channel users in global list */
if (server->server_type != SILC_SERVER)
silc_server_announce_get_channels(server, server->global_list,
- &channels, &channel_users,
+ &channels, &channel_modes,
+ &channel_users,
&channel_users_modes,
&channel_users_modes_c,
&channel_topics,
silc_buffer_free(channels);
}
+ if (channel_modes) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_modes[i])
+ continue;
+ silc_buffer_push(channel_modes[i],
+ channel_modes[i]->data -
+ channel_modes[i]->head);
+ SILC_LOG_HEXDUMP(("channel modes"), channel_modes[i]->data,
+ channel_modes[i]->len);
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_modes[i]->data,
+ channel_modes[i]->len,
+ FALSE);
+ silc_buffer_free(channel_modes[i]);
+ }
+ silc_free(channel_modes);
+ }
+
if (channel_users) {
silc_buffer_push(channel_users, channel_users->data - channel_users->head);
SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
server->cmd_ident, 1,
- 3, idp->data, idp->len);
+ 4, idp->data, idp->len);
silc_server_packet_send(server, client ? client->router->connection :
server->router->connection,
SILC_PACKET_COMMAND, 0,