int silc_server_init(SilcServer server)
{
- int *sock = NULL, sock_count = 0, i;
+ int *sock = NULL, sock_count, i;
SilcServerID *id;
SilcServerEntry id_entry;
SilcIDListPurge purge;
tmp = silc_net_create_server(server->config->listen_port->port,
server->config->listen_port->listener_ip);
+
if (tmp < 0) {
SILC_LOG_ERROR(("Could not create server listener: %s on %d",
server->config->listen_port->listener_ip,
goto err0;
}
- sock = silc_realloc(sock, (sizeof(int *) * (sock_count + 1)));
+ sock = silc_realloc(sock, sizeof(*sock) * (sock_count + 1));
sock[sock_count] = tmp;
sock_count++;
listen = listen->next;
silc_net_set_socket_nonblock(sock[i]);
server->sock = sock[i];
+ /* 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[i], SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+ server->sockets[sock[i]] = newsocket;
+
+ /* Perform name and address lookups to resolve the listenning address
+ and port. */
+ if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname,
+ &newsocket->ip)) {
+ if ((server->params->require_reverse_mapping && !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 err0;
+ }
+ if (!newsocket->hostname)
+ newsocket->hostname = strdup(newsocket->ip);
+ }
+ newsocket->port = silc_net_get_local_port(sock[i]);
+
/* Create a Server ID for the server. */
- silc_id_create_server_id(sock[i], server->rng, &id);
- if (!id) {
+ silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
+ if (!id)
goto err0;
- }
server->id = id;
server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
}
id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
- /* 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[i], SILC_SOCKET_TYPE_SERVER, id_entry,
- &newsocket);
-
- server->sockets[sock[i]] = newsocket;
-
- /* Perform name and address lookups to resolve the listenning address
- and port. */
- if (!silc_net_check_local_by_sock(sock[i], &newsocket->hostname,
- &newsocket->ip)) {
- if ((server->params->require_reverse_mapping && !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 err0;
- }
- if (!newsocket->hostname)
- newsocket->hostname = strdup(newsocket->ip);
- }
- newsocket->port = silc_net_get_local_port(sock[i]);
-
/* Put the allocated socket pointer also to the entry allocated above
for fast back-referencing to the socket list. */
- id_entry->connection = (void *)server->sockets[sock[i]];
+ newsocket->user_data = (void *)id_entry;
+ id_entry->connection = (void *)newsocket;
server->id_entry = id_entry;
}
purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->local_list->clients;
purge->schedule = server->schedule;
+ purge->timeout = 600;
silc_schedule_task_add(purge->schedule, 0,
silc_idlist_purge,
- (void *)purge, 600, 0,
+ (void *)purge, purge->timeout, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
/* Clients global list */
purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->global_list->clients;
purge->schedule = server->schedule;
+ purge->timeout = 300;
silc_schedule_task_add(purge->schedule, 0,
silc_idlist_purge,
- (void *)purge, 300, 0,
+ (void *)purge, purge->timeout, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
SILC_LOG_DEBUG(("Server initialized"));
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. */
+/* Fork server to background and set gid+uid to non-root */
void silc_server_daemonise(SilcServer server)
+{
+ int i;
+
+ i = fork ();
+
+ if (i) {
+ if (i > 0) {
+ if (geteuid())
+ SILC_LOG_DEBUG(("Server started as user"));
+ else
+ SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
+
+ SILC_LOG_DEBUG(("Forking SILC server to background"));
+ exit(0);
+ } else {
+ SILC_LOG_DEBUG(("fork() failed, cannot proceed"));
+ exit(1);
+ }
+ }
+ setsid();
+}
+
+/* Drop root privligies. If this cannot be done, die. */
+
+void silc_server_drop(SilcServer server)
{
/* Are we executing silcd as root or a regular user? */
- if (geteuid()==0) {
-
+ if (!geteuid()) {
+
struct passwd *pw;
struct group *gr;
char *user, *group;
-
+
if (!server->config->identity || !server->config->identity->user ||
!server->config->identity->group) {
fprintf(stderr, "Error:"
"\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"));
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) {
+ if (!(pw=getpwnam(user))) {
fprintf(stderr, "No such user %s found\n", user);
exit(1);
}
- if (!gr) {
+ if (!(gr=getgrnam(group))) {
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) {
"\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) {
+ if (setgid(gr->gr_gid)==0) {
SILC_LOG_DEBUG(("Setgid to %s", group));
} else {
SILC_LOG_DEBUG(("Setgid to %s failed", group));
group);
exit(1);
}
- SILC_LOG_DEBUG(("Changing to user nobody"));
- if(setuid(pw->pw_uid)==0) {
+#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
+ if (setgroups(0, NULL)!=0) {
+ SILC_LOG_DEBUG(("Setgroups to NULL failed"));
+ fprintf(stderr, "Tried to setgroups NULL but failed. Exiting\n");
+ exit(1);
+ }
+ if (initgroups(user, gr->gr_gid)!=0) {
+ SILC_LOG_DEBUG(("Initgroups to user %s (gid=%d) failed", user, gr->gr_gid));
+ fprintf(stderr, "Tried to initgroups %s (gid=%d) but no such user. Exiting\n",
+ user, gr->gr_gid);
+ exit(1);
+ }
+#endif
+ SILC_LOG_DEBUG(("Changing to user %s", user));
+ if (setuid(pw->pw_uid)==0) {
SILC_LOG_DEBUG(("Setuid to %s", user));
} else {
SILC_LOG_DEBUG(("Setuid to %s failed", 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();
}
}
if (sconn->retry_count > server->params->retry_count &&
server->params->retry_keep_trying == FALSE) {
SILC_LOG_ERROR(("Could not connect to router, giving up"));
+ silc_free(sconn->remote_host);
+ silc_free(sconn);
return;
}
/* We now have the key material as the result of the key exchange
protocol. Take the key material into use. Free the raw key material
as soon as we've set them into use. */
- if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+ if (!silc_server_protocol_ke_set_keys(server, ctx->ske,
+ ctx->sock, ctx->keymat,
ctx->ske->prop->cipher,
ctx->ske->prop->pkcs,
ctx->ske->prop->hash,
silc_packet_context_free(ctx->packet);
if (ctx->ske)
silc_ske_free(ctx->ske);
+ silc_free(ctx->auth_data);
silc_free(ctx);
}
/* We now have the key material as the result of the key exchange
protocol. Take the key material into use. Free the raw key material
as soon as we've set them into use. */
- if (!silc_server_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
+ if (!silc_server_protocol_ke_set_keys(server, ctx->ske,
+ ctx->sock, ctx->keymat,
ctx->ske->prop->cipher,
ctx->ske->prop->pkcs,
ctx->ske->prop->hash,
and other information is created after we have received NEW_CLIENT
packet from client. */
client = silc_idlist_add_client(server->local_list,
- NULL, NULL, NULL, NULL, NULL, sock);
+ NULL, NULL, NULL, NULL, NULL, sock, 0);
if (!client) {
SILC_LOG_ERROR(("Could not add new client to cache"));
silc_free(sock->user_data);
process all packets synchronously, since there might be packets in
queue that we are not able to decrypt without first processing the
packets before them. */
- if (sock->protocol && sock->protocol->protocol &&
- (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
- sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) {
+ if ((parser_context->packet->type == SILC_PACKET_REKEY ||
+ parser_context->packet->type == SILC_PACKET_REKEY_DONE) ||
+ (sock->protocol && sock->protocol->protocol &&
+ (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE ||
+ sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) {
silc_server_packet_parse_real(server->schedule, 0, sock->sock,
parser_context);
become invalid now as well. */
if (user_data->id)
silc_server_remove_clients_by_server(server, user_data, TRUE);
+ if (server->server_type == SILC_SERVER)
+ silc_server_remove_channels_by_server(server, user_data);
} else {
/* Update the client entries of this server to the new backup
router. This also removes the clients that *really* was owned
silc_server_update_clients_by_server(server, user_data, backup_router,
TRUE, TRUE);
silc_server_update_servers_by_server(server, user_data, backup_router);
+ if (server->server_type == SILC_SERVER)
+ silc_server_update_channels_by_server(server, user_data,
+ backup_router);
}
/* Free the server entry */
}
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, newhmac);
+ NULL, key, newhmac, 0);
if (!entry) {
silc_free(channel_name);
silc_cipher_free(key);
/* Create the channel */
entry = silc_idlist_add_channel(server->local_list, channel_name,
SILC_CHANNEL_MODE_NONE, channel_id,
- NULL, key, newhmac);
+ NULL, key, newhmac, 0);
if (!entry) {
silc_free(channel_name);
return NULL;
SILC_LOG_DEBUG(("Start"));
/* Decode channel key payload */
- payload = silc_channel_key_payload_parse(key_payload);
+ payload = silc_channel_key_payload_parse(key_payload->data,
+ key_payload->len);
if (!payload) {
SILC_LOG_ERROR(("Bad channel key payload, dropped"));
channel = NULL;
}
}
+static SilcBuffer
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+{
+ va_list ap;
+ SilcBuffer p;
+
+ va_start(ap, argc);
+ p = silc_notify_payload_encode(notify, argc, ap);
+ va_end(ap);
+
+ return p;
+}
+
/* This function is used by router to announce existing servers to our
primary router when we've connected to it. If `creation_time' is non-zero
then only the servers that has been created after the `creation_time'
static void silc_server_announce_get_clients(SilcServer server,
SilcIDList id_list,
SilcBuffer *clients,
+ SilcBuffer *umodes,
unsigned long creation_time)
{
SilcIDCacheList list;
SilcIDCacheEntry id_cache;
SilcClientEntry client;
SilcBuffer idp;
+ SilcBuffer tmp;
+ unsigned char mode[4];
/* Go through all clients in the list */
if (silc_idcache_get_all(id_list->clients, &list)) {
silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
silc_buffer_put(*clients, idp->data, idp->len);
silc_buffer_pull(*clients, idp->len);
+
+ SILC_PUT32_MSB(client->mode, mode);
+ tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+ 2, idp->data, idp->len,
+ mode, 4);
+ *umodes = silc_buffer_realloc(*umodes,
+ (*umodes ?
+ (*umodes)->truelen + tmp->len :
+ tmp->len));
+ silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+ silc_buffer_put(*umodes, tmp->data, tmp->len);
+ silc_buffer_pull(*umodes, tmp->len);
+ silc_buffer_free(tmp);
+
silc_buffer_free(idp);
if (!silc_idcache_list_next(list, &id_cache))
SilcSocketConnection remote)
{
SilcBuffer clients = NULL;
+ SilcBuffer umodes = NULL;
SILC_LOG_DEBUG(("Announcing clients"));
/* Get clients in local list */
silc_server_announce_get_clients(server, server->local_list,
- &clients, creation_time);
+ &clients, &umodes, creation_time);
/* 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, creation_time);
+ &clients, &umodes, creation_time);
if (clients) {
silc_buffer_push(clients, clients->data - clients->head);
silc_buffer_free(clients);
}
+
+ if (umodes) {
+ silc_buffer_push(umodes, umodes->data - umodes->head);
+ SILC_LOG_HEXDUMP(("umodes"), umodes->data, umodes->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ umodes->data, umodes->len, TRUE);
+
+ silc_buffer_free(umodes);
+ }
}
-static SilcBuffer
-silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+/* Returns channel's topic for announcing it */
+
+void silc_server_announce_get_channel_topic(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *topic)
{
- va_list ap;
- SilcBuffer p;
+ SilcBuffer chidp;
- va_start(ap, argc);
- p = silc_notify_payload_encode(notify, argc, ap);
- va_end(ap);
-
- return p;
+ if (channel->topic) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+ chidp->data, chidp->len,
+ channel->topic,
+ strlen(channel->topic));
+ silc_buffer_free(chidp);
+ }
}
/* Returns assembled packets for channel users of the `channel'. */
SilcBuffer *channel_users,
SilcBuffer **channel_users_modes,
uint32 *channel_users_modes_c,
+ SilcBuffer **channel_topics,
SilcChannelID ***channel_ids,
unsigned long creation_time)
{
silc_buffer_pull(*channels, len);
}
+ /* Channel user modes */
*channel_users_modes = silc_realloc(*channel_users_modes,
sizeof(**channel_users_modes) *
(i + 1));
(*channel_ids)[i] = NULL;
silc_server_announce_get_channel_users(server, channel,
channel_users,
- channel_users_modes[i]);
+ &(*channel_users_modes)[i]);
(*channel_ids)[i] = channel->id;
+
+ /* Channel's topic */
+ *channel_topics = silc_realloc(*channel_topics,
+ sizeof(**channel_topics) * (i + 1));
+ (*channel_topics)[i] = NULL;
+ silc_server_announce_get_channel_topic(server, channel,
+ &(*channel_topics)[i]);
i++;
if (!silc_idcache_list_next(list, &id_cache))
{
SilcBuffer channels = NULL, channel_users = NULL;
SilcBuffer *channel_users_modes = NULL;
+ SilcBuffer *channel_topics = NULL;
uint32 channel_users_modes_c = 0;
SilcChannelID **channel_ids = NULL;
&channels, &channel_users,
&channel_users_modes,
&channel_users_modes_c,
+ &channel_topics,
&channel_ids, creation_time);
/* Get channels and channel users in global list */
- silc_server_announce_get_channels(server, server->global_list,
- &channels, &channel_users,
- &channel_users_modes,
- &channel_users_modes_c,
- &channel_ids, creation_time);
+ if (server->server_type != SILC_SERVER)
+ silc_server_announce_get_channels(server, server->global_list,
+ &channels, &channel_users,
+ &channel_users_modes,
+ &channel_users_modes_c,
+ &channel_topics,
+ &channel_ids, creation_time);
if (channels) {
silc_buffer_push(channels, channels->data - channels->head);
silc_buffer_free(channel_users_modes[i]);
}
silc_free(channel_users_modes);
- silc_free(channel_ids);
}
+
+ if (channel_topics) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_topics[i])
+ continue;
+
+ silc_buffer_push(channel_topics[i],
+ channel_topics[i]->data -
+ channel_topics[i]->head);
+ SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data,
+ channel_topics[i]->len);
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_topics[i]->data,
+ channel_topics[i]->len,
+ FALSE);
+ silc_buffer_free(channel_topics[i]);
+ }
+ silc_free(channel_topics);
+ }
+
+ silc_free(channel_ids);
}
/* Failure timeout callback. If this is called then we will immediately
uint32 user_count)
{
int i;
+ uint16 idp_len;
+ uint32 mode;
+ SilcClientID *client_id;
+ SilcClientEntry client;
+ SilcIDCacheEntry cache;
+ bool global;
for (i = 0; i < user_count; i++) {
- uint16 idp_len;
- uint32 mode;
- SilcClientID *client_id;
- SilcClientEntry client;
-
/* Client ID */
SILC_GET16_MSB(idp_len, user_list->data + 2);
idp_len += 4;
silc_free(client_id);
continue;
}
+
+ global = FALSE;
/* Check if we have this client cached already. */
client = silc_idlist_find_client_by_id(server->local_list, client_id,
- server->server_type, NULL);
- if (!client)
+ server->server_type, &cache);
+ if (!client) {
client = silc_idlist_find_client_by_id(server->global_list,
client_id, server->server_type,
- NULL);
+ &cache);
+ global = TRUE;
+ }
if (!client) {
/* If router did not find such Client ID in its lists then this must
be bogus client or some router in the net is buggy. */
global. */
client = silc_idlist_add_client(server->global_list, NULL, NULL, NULL,
silc_id_dup(client_id, SILC_ID_CLIENT),
- sock->user_data, NULL);
+ sock->user_data, NULL, 0);
if (!client) {
SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
silc_free(client_id);
}
client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ } else {
+ /* Found, if it is from global list we'll assure that we won't
+ expire it now that the entry is on channel. */
+ if (global)
+ cache->expire = 0;
}
silc_free(client_id);