XXX This function will become more general and will support multiple
listening ports */
-static bool silc_server_listen(SilcServer server, int *sock)
+static bool silc_server_listen(SilcServer server, const char *server_ip,
+ SilcUInt16 port, int *sock)
{
-
- *sock = silc_net_create_server(server->config->server_info->port,
- server->config->server_info->server_ip);
+ *sock = silc_net_create_server(port, server_ip);
if (*sock < 0) {
SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
- server->config->server_info->server_ip,
- server->config->server_info->port));
+ server_ip, port));
return FALSE;
}
return TRUE;
}
+/* Adds a secondary listener. */
+bool silc_server_init_secondary(SilcServer server)
+{
+ int sock=0, sock_list[server->config->param.connections_max];
+ SilcSocketConnection newsocket = NULL;
+ SilcServerConfigServerInfoInterface *interface;
+
+ for (interface = server->config->server_info->secondary; interface;
+ interface = interface->next, sock++) {
+
+ if (!silc_server_listen(server,
+ interface->server_ip, interface->port, &sock_list[sock]))
+ goto err;
+
+ /* Set socket to non-blocking mode */
+ silc_net_set_socket_nonblock(sock_list[sock]);
+
+ /* Add ourselves also to the socket table. The entry allocated above
+ is sent as argument for fast referencing in the future. */
+ silc_socket_alloc(sock_list[sock],
+ SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+ server->sockets[sock_list[sock]] = newsocket;
+
+ /* Perform name and address lookups to resolve the listenning address
+ and port. */
+ if (!silc_net_check_local_by_sock(sock_list[sock], &newsocket->hostname,
+ &newsocket->ip)) {
+ if ((server->config->require_reverse_lookup && !newsocket->hostname) ||
+ !newsocket->ip) {
+ SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+ newsocket->hostname ? newsocket->hostname :
+ newsocket->ip ? newsocket->ip : ""));
+ server->stat.conn_failures++;
+ goto err;
+ }
+ if (!newsocket->hostname)
+ newsocket->hostname = strdup(newsocket->ip);
+ }
+ newsocket->port = silc_net_get_local_port(sock);
+
+ newsocket->user_data = (void *)server->id_entry;
+ silc_schedule_task_add(server->schedule, sock_list[sock],
+ silc_server_accept_new_connection,
+ (void *)server, 0, 0,
+ SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL);
+
+ }
+
+ return TRUE;
+
+err:
+
+ do silc_net_close_server(sock_list[sock--]); while (sock >= 0);
+ return FALSE;
+
+}
+
/* Initializes the entire SILC server. This is called always before running
the server. This is called only once at the initialization of the program.
This binds the server to its listenning port. After this function returns
goto err;
/* Create a listening server */
- if (!silc_server_listen(server, &sock))
+ if (!silc_server_listen(server,
+ server->config->server_info->primary == NULL ? NULL :
+ server->config->server_info->primary->server_ip,
+ server->config->server_info->primary == NULL ? 0 :
+ server->config->server_info->primary->port,
+ &sock))
goto err;
/* Set socket to non-blocking mode */
(void *)server, 0, 0,
SILC_TASK_FD,
SILC_TASK_PRI_NORMAL);
+
+ if (silc_server_init_secondary(server) == FALSE)
+ goto err;
+
server->listenning = TRUE;
/* If server connections has been configured then we must be router as
silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
/* Connect to remote host */
- sock = silc_net_create_connection(server->config->server_info->server_ip,
- sconn->remote_port,
- sconn->remote_host);
+ sock = silc_net_create_connection(server->config->server_info->primary == NULL ? NULL :
+ server->config->server_info->primary->server_ip,
+ sconn->remote_port,
+ sconn->remote_host);
if (sock < 0) {
SILC_LOG_ERROR(("Could not connect to router %s:%d",
sconn->remote_host, sconn->remote_port));
silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
void *context)
{
- SilcServer server = (SilcServer)context;
- SilcServerKEInternalContext *proto_ctx;
+ SilcServerKEInternalContext *proto_ctx =
+ (SilcServerKEInternalContext *)context;
+ SilcServer server = (SilcServer)proto_ctx->server;
SilcServerConfigClient *cconfig = NULL;
SilcServerConfigServer *sconfig = NULL;
SilcServerConfigRouter *rconfig = NULL;
SilcServerConfigDeny *deny;
int port;
+ context = (void *)server;
+
SILC_LOG_DEBUG(("Start"));
/* Check whether we could resolve both IP and FQDN. */
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
"Unknown host or IP");
+ silc_free(proto_ctx);
return;
}
SILC_LOG_INFO(("Incoming connection %s (%s)", sock->hostname,
sock->ip));
- port = server->sockets[server->sock]->port; /* Listenning port */
+ /* Listenning port */
+ port = server->sockets[(SilcUInt32)proto_ctx->context]->port;
/* Check whether this connection is denied to connect to us. */
deny = silc_server_config_find_denied(server, sock->ip);
SILC_STATUS_ERR_BANNED_FROM_SERVER,
deny->reason);
server->stat.conn_failures++;
+ silc_free(proto_ctx);
return;
}
sconfig = silc_server_config_find_server_conn(server, sock->hostname);
if (server->server_type == SILC_ROUTER) {
if (!(rconfig = silc_server_config_find_router_conn(server,
- sock->ip, port)))
+ sock->ip, sock->port)))
rconfig = silc_server_config_find_router_conn(server, sock->hostname,
sock->port);
}
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_BANNED_FROM_SERVER);
server->stat.conn_failures++;
+ silc_free(proto_ctx);
return;
}
/* The connection is allowed */
- /* Allocate internal context for key exchange protocol. This is
+ /* Set internal context for key exchange protocol. This is
sent as context for the protocol. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->server = context;
proto_ctx->sock = sock;
proto_ctx->rng = server->rng;
proto_ctx->responder = TRUE;
proto_ctx->timeout_task =
silc_schedule_task_add(server->schedule, sock->sock,
silc_server_timeout_remote,
- context, server->config->key_exchange_timeout, 0,
+ (void *)server,
+ server->config->key_exchange_timeout, 0,
SILC_TASK_TIMEOUT,
SILC_TASK_PRI_LOW);
}
{
SilcServer server = (SilcServer)context;
SilcSocketConnection newsocket;
+ SilcServerKEInternalContext *proto_ctx;
int sock;
SILC_LOG_DEBUG(("Accepting new connection"));
server->stat.conn_attempts++;
- sock = silc_net_accept_connection(server->sock);
+ sock = silc_net_accept_connection(fd);
if (sock < 0) {
SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
server->stat.conn_failures++;
/* Perform asynchronous host lookup. This will lookup the IP and the
FQDN of the remote connection. After the lookup is done the connection
is accepted further. */
+ proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+ proto_ctx->server = server;
+ proto_ctx->context = (void *)fd;
silc_socket_host_lookup(newsocket, TRUE,
- silc_server_accept_new_connection_lookup, context,
- server->schedule);
+ silc_server_accept_new_connection_lookup,
+ (void *)proto_ctx, server->schedule);
}
/* Second part of accepting new connection. Key exchange protocol has been
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) {
+ /* 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 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);
+ /* 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);
- /* 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 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 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;
}
void silc_server_announce_get_channel_users(SilcServer server,
SilcChannelEntry channel,
+ SilcBuffer *channel_modes,
SilcBuffer *channel_users,
SilcBuffer *channel_users_modes)
{
SilcChannelClientEntry chl;
SilcHashTableList htl;
- SilcBuffer chidp, clidp;
+ SilcBuffer chidp, clidp, csidp;
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);
+ csidp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+
+ /* CMODE notify */
+ 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, csidp->data, csidp->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_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,
- mode, 4,
- clidp->data, clidp->len);
+ 4, csidp->data, csidp->len,
+ mode, sizeof(mode),
+ 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);
silc_buffer_free(chidp);
+ silc_buffer_free(csidp);
}
/* Returns assembled packets for all channels and users on those channels
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,
/* Assembles user list and users mode list from the `channel'. */
-void silc_server_get_users_on_channel(SilcServer server,
+bool silc_server_get_users_on_channel(SilcServer server,
SilcChannelEntry channel,
SilcBuffer *user_list,
SilcBuffer *mode_list,
SilcBuffer idp;
SilcUInt32 list_count = 0, len = 0;
+ if (!silc_hash_table_count(channel->user_list))
+ return FALSE;
+
silc_hash_table_list(channel->user_list, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl))
len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
*user_list = client_id_list;
*mode_list = client_mode_list;
*user_count = list_count;
+ return TRUE;
}
/* Saves users and their modes to the `channel'. */
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,