SILC_TASK_CALLBACK(silc_server_channel_key_rekey);
SILC_TASK_CALLBACK(silc_server_failure_callback);
SILC_TASK_CALLBACK(silc_server_rekey_callback);
+SILC_TASK_CALLBACK(silc_server_get_stats);
/* Allocates a new SILC server object. This has to be done before the server
can be used. After allocation one must call silc_server_init to initialize
void silc_server_free(SilcServer server)
{
- if (server) {
+ if (!server)
+ return;
+
#ifdef SILC_SIM
+ {
SilcSim sim;
-
+
while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) {
silc_dlist_del(server->sim, sim);
silc_sim_free(sim);
}
silc_dlist_uninit(server->sim);
+ }
#endif
- silc_server_config_unref(&server->config_ref);
- if (server->rng)
- silc_rng_free(server->rng);
- if (server->pkcs)
- silc_pkcs_free(server->pkcs);
- if (server->public_key)
- silc_pkcs_public_key_free(server->public_key);
- if (server->private_key)
- silc_pkcs_private_key_free(server->private_key);
- if (server->pending_commands)
- silc_dlist_uninit(server->pending_commands);
- if (server->id_entry)
- silc_idlist_del_server(server->local_list, server->id_entry);
-
- silc_idcache_free(server->local_list->clients);
- silc_idcache_free(server->local_list->servers);
- silc_idcache_free(server->local_list->channels);
- silc_idcache_free(server->global_list->clients);
- silc_idcache_free(server->global_list->servers);
- silc_idcache_free(server->global_list->channels);
-
- silc_free(server->sockets);
- silc_free(server);
- }
+ silc_server_config_unref(&server->config_ref);
+ if (server->rng)
+ silc_rng_free(server->rng);
+ if (server->pkcs)
+ silc_pkcs_free(server->pkcs);
+ if (server->public_key)
+ silc_pkcs_public_key_free(server->public_key);
+ if (server->private_key)
+ silc_pkcs_private_key_free(server->private_key);
+ if (server->pending_commands)
+ silc_dlist_uninit(server->pending_commands);
+ if (server->id_entry)
+ silc_idlist_del_server(server->local_list, server->id_entry);
+
+ silc_idcache_free(server->local_list->clients);
+ silc_idcache_free(server->local_list->servers);
+ silc_idcache_free(server->local_list->channels);
+ silc_idcache_free(server->global_list->clients);
+ silc_idcache_free(server->global_list->servers);
+ silc_idcache_free(server->global_list->channels);
+ silc_hash_table_free(server->watcher_list);
+
+ silc_hash_free(server->md5hash);
+ silc_hash_free(server->sha1hash);
+ silc_hmac_unregister_all();
+ silc_hash_unregister_all();
+ silc_cipher_unregister_all();
+ silc_pkcs_unregister_all();
+
+ silc_free(server->local_list);
+ silc_free(server->global_list);
+ silc_free(server->server_name);
+ silc_free(server->id_string);
+ silc_free(server->purge_i);
+ silc_free(server->purge_g);
+ silc_free(server);
}
-/* Opens a listening port.
- XXX This function will become more general and will support multiple
- listening ports */
+/* Creates a new server listener. */
-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
SilcSocketConnection newsocket = NULL;
SILC_LOG_DEBUG(("Initializing server"));
- assert(server);
- assert(server->config);
+ server->starttime = time(NULL);
+
+ /* Take config object for us */
silc_server_config_ref(&server->config_ref, server->config,
server->config);
- /* Set public and private keys */
- if (!server->config->server_info ||
- !server->config->server_info->public_key ||
- !server->config->server_info->private_key) {
- SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
- return FALSE;
- }
-
/* Steal public and private key from the config object */
server->public_key = server->config->server_info->public_key;
server->private_key = server->config->server_info->private_key;
server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
+ /* Init watcher list */
+ server->watcher_list =
+ silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
+ silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
+ NULL, NULL, TRUE);
+ if (!server->watcher_list)
+ 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 */
goto err;
}
id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
-
+
/* Put the allocated socket pointer also to the entry allocated above
for fast back-referencing to the socket list. */
newsocket->user_data = (void *)id_entry;
timeout. It expires as soon as the caller calls silc_server_run. This
task performs authentication protocol and key exchange with our
primary router. */
- silc_schedule_task_add(server->schedule, sock,
+ silc_schedule_task_add(server->schedule, 0,
silc_server_connect_to_router,
(void *)server, 0, 1,
SILC_TASK_TIMEOUT,
(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
and removes the expired cache entries. */
/* Clients local list */
- purge = silc_calloc(1, sizeof(*purge));
+ server->purge_i = purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->local_list->clients;
purge->schedule = server->schedule;
purge->timeout = 600;
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
/* Clients global list */
- purge = silc_calloc(1, sizeof(*purge));
+ server->purge_g = purge = silc_calloc(1, sizeof(*purge));
purge->cache = server->global_list->clients;
purge->schedule = server->schedule;
purge->timeout = 300;
(void *)purge, purge->timeout, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+ /* If we are normal server we'll retrieve network statisticial information
+ once in a while from the router. */
+ if (server->server_type == SILC_SERVER)
+ silc_schedule_task_add(purge->schedule, 0, silc_server_get_stats,
+ server, 10, 0, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+
+ if (server->server_type == SILC_ROUTER)
+ server->stat.routers++;
+
SILC_LOG_DEBUG(("Server initialized"));
/* We are done here, return succesfully */
return TRUE;
err:
+ silc_server_config_unref(&server->config_ref);
silc_net_close_server(sock);
return FALSE;
}
{
SilcServerConfig newconfig;
- /* Our old config is gone now. We'll unreference our reference made in
- silc_server_init and then destroy it since we are destroying it
- underneath the application (layer which called silc_server_init). */
- silc_server_config_unref(&server->config_ref);
- silc_server_config_destroy(server->config);
+ SILC_LOG_INFO(("Rehashing server"));
/* Reset the logging system */
silc_log_quick = TRUE;
silc_log_flush_all();
/* Start the main rehash phase (read again the config file) */
- SILC_LOG_INFO(("Rehashing server"));
newconfig = silc_server_config_alloc(server->config_file);
if (!newconfig) {
SILC_LOG_ERROR(("Rehash FAILED."));
return FALSE;
}
- /* Take new config context */
- server->config = newconfig;
- silc_server_config_ref(&server->config_ref, server->config, server->config);
-
- /* Set public and private keys */
- if (!server->config->server_info ||
- !server->config->server_info->public_key ||
- !server->config->server_info->private_key) {
- SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
- return FALSE;
- }
+ /* Reinit scheduler if necessary */
+ if (newconfig->param.connections_max > server->config->param.connections_max)
+ if (!silc_schedule_reinit(server->schedule,
+ newconfig->param.connections_max))
+ return FALSE;
/* Fix the server_name field */
- if (!strcmp(server->server_name, newconfig->server_info->server_name)) {
- /* We don't need any update */
- silc_free(newconfig->server_info->server_name);
- newconfig->server_info->server_name = NULL;
- } else {
+ if (strcmp(server->server_name, newconfig->server_info->server_name)) {
silc_free(server->server_name);
server->server_name = newconfig->server_info->server_name;
newconfig->server_info->server_name = NULL;
/* Update the idcache list with a fresh pointer */
silc_free(server->id_entry->server_name);
server->id_entry->server_name = strdup(server->server_name);
- silc_idcache_del_by_context(server->local_list->servers, server->id_entry);
- silc_idcache_add(server->local_list->servers,
- server->id_entry->server_name,
- server->id_entry->id, server->id_entry, 0, NULL);
+ if (!silc_idcache_del_by_context(server->local_list->servers,
+ server->id_entry))
+ return FALSE;
+ if (!silc_idcache_add(server->local_list->servers,
+ server->id_entry->server_name,
+ server->id_entry->id, server->id_entry, 0, NULL))
+ return FALSE;
}
+ /* Set logging */
silc_server_config_setlogfiles(server);
/* Change new key pair if necessary */
- if (server->config->server_info->public_key &&
+ if (newconfig->server_info->public_key &&
!silc_pkcs_public_key_compare(server->public_key,
- server->config->server_info->public_key)) {
+ newconfig->server_info->public_key)) {
silc_pkcs_public_key_free(server->public_key);
silc_pkcs_private_key_free(server->private_key);
- server->public_key = server->config->server_info->public_key;
- server->private_key = server->config->server_info->private_key;
- server->config->server_info->public_key = NULL;
- server->config->server_info->private_key = NULL;
+ server->public_key = newconfig->server_info->public_key;
+ server->private_key = newconfig->server_info->private_key;
+ newconfig->server_info->public_key = NULL;
+ newconfig->server_info->private_key = NULL;
/* Allocate PKCS context for local public and private keys */
silc_pkcs_free(server->pkcs);
silc_pkcs_private_key_set(server->pkcs, server->private_key);
}
- return TRUE;
-}
-
-/* Drop root privileges. If some system call fails, die. */
-
-void silc_server_drop(SilcServer server)
-{
- /* Are we executing silcd as root or a regular user? */
- if (geteuid()) {
- SILC_LOG_DEBUG(("Server started as user"));
- }
- else {
- struct passwd *pw;
- struct group *gr;
- char *user, *group;
-
- SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
-
- /* Get the values given for user and group in configuration file */
- user = server->config->server_info->user;
- group = server->config->server_info->group;
-
- if (!user || !group) {
- fprintf(stderr, "Error:"
- "\tSILC server must not be run as root. For the security of your\n"
- "\tsystem it is strongly suggested that you run SILC under dedicated\n"
- "\tuser account. Modify the ServerInfo configuration section to run\n"
- "\tthe server as non-root user.\n");
- exit(1);
- }
-
- /* Check whether the user/group does not begin with a number */
- if (isdigit(user[0]) || isdigit(group[0])) {
- SILC_LOG_DEBUG(("User and/or group starts with a number"));
- fprintf(stderr, "Invalid user and/or group information\n");
- fprintf(stderr, "Please assign them as names, not numbers\n");
- exit(1);
- }
-
- if (!(pw = getpwnam(user))) {
- fprintf(stderr, "Error: No such user %s found.\n", user);
- exit(1);
- }
- if (!(gr = getgrnam(group))) {
- fprintf(stderr, "Error: No such group %s found.\n", group);
- exit(1);
- }
+ /* Go through all configured routers after rehash */
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_to_router,
+ (void *)server, 0, 1,
+ SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
- /* 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)) {
- fprintf(stderr, "Error:"
- "\tSILC server must not be run as root. For the security of your\n"
- "\tsystem it is strongly suggested that you run SILC under dedicated\n"
- "\tuser account. Modify the ServerInfo configuration section to run\n"
- "\tthe server as non-root user.\n");
- exit(1);
- }
+ /* Check whether our router status has changed */
+ if (newconfig->servers) {
+ SilcServerConfigServer *ptr = newconfig->servers;
- SILC_LOG_DEBUG(("Changing to group %s (gid=%u)", group, gr->gr_gid));
- if (setgid(gr->gr_gid) != 0) {
- fprintf(stderr, "Error: Failed setgid() to %s (gid=%u). Exiting.\n",
- group, gr->gr_gid);
- exit(1);
- }
-#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
- SILC_LOG_DEBUG(("Removing supplementary groups"));
- if (setgroups(0, NULL) != 0) {
- fprintf(stderr, "Error: Failed setgroups() to NULL. Exiting.\n");
- exit(1);
- }
- SILC_LOG_DEBUG(("Setting supplementary groups for user %s", user));
- if (initgroups(user, gr->gr_gid) != 0) {
- fprintf(stderr, "Error: Failed initgroups() for user %s (gid=%u). "
- "Exiting.\n", user, gr->gr_gid);
- exit(1);
- }
-#endif
- SILC_LOG_DEBUG(("Changing to user %s (uid=%u)", user, pw->pw_uid));
- if (setuid(pw->pw_uid) != 0) {
- fprintf(stderr, "Error: Failed to setuid() to %s (gid=%u). Exiting.\n",
- user, pw->pw_uid);
- exit(1);
+ server->server_type = SILC_ROUTER;
+ while (ptr) {
+ if (ptr->backup_router) {
+ server->server_type = SILC_BACKUP_ROUTER;
+ server->backup_router = TRUE;
+ server->id_entry->server_type = SILC_BACKUP_ROUTER;
+ break;
+ }
+ ptr = ptr->next;
}
}
-}
-
-/* Fork server to background */
-
-void silc_server_daemonise(SilcServer server)
-{
- int i;
-
- SILC_LOG_DEBUG(("Forking SILC server to background"));
- if ((i = fork()) < 0) {
- fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
- exit(1);
- }
+ /* Our old config is gone now. We'll unreference our reference made in
+ silc_server_init and then destroy it since we are destroying it
+ underneath the application (layer which called silc_server_init). */
+ silc_server_config_unref(&server->config_ref);
+ silc_server_config_destroy(server->config);
- if (i) /* Kill the parent */
- exit(0);
+ /* Take new config context */
+ server->config = newconfig;
+ silc_server_config_ref(&server->config_ref, server->config, server->config);
- server->background = TRUE;
- setsid();
+ SILC_LOG_DEBUG(("Server rehashed"));
- /* XXX close stdin, stdout, stderr -- before this, check that all writes
- to stderr are changed to SILC_SERVER_LOG_ERROR() */
+ return TRUE;
}
/* The heart of the server. This runs the scheduler thus runs the server.
SILC_LOG_DEBUG(("Stopping server"));
if (server->schedule) {
+ int i;
+
+ for (i = 0; i < server->config->param.connections_max; i++) {
+ if (!server->sockets[i])
+ continue;
+ silc_socket_free(server->sockets[i]);
+ }
+ silc_free(server->sockets);
+ server->sockets = NULL;
+
silc_schedule_stop(server->schedule);
silc_schedule_uninit(server->schedule);
server->schedule = NULL;
/* Cancel any possible retry timeouts */
silc_schedule_task_del_by_callback(server->schedule,
- silc_server_connect_router);
+ silc_server_connect_to_router_retry);
/* Set socket options */
silc_net_set_socket_nonblock(sock);
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 :
+ 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));
return;
}
+ /* Cancel any possible retry timeouts */
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_router);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_to_router_retry);
+
/* Create the connections to all our routes */
for (ptr = server->config->routers; ptr; ptr = ptr->next) {
ptr->host, ptr->port));
if (ptr->initiator) {
+ /* Check whether we are connected to this host already */
+ if (silc_server_num_sockets_by_remote(server,
+ silc_net_is_ip(ptr->host) ?
+ ptr->host : NULL,
+ silc_net_is_ip(ptr->host) ?
+ NULL : ptr->host, ptr->port,
+ SILC_SOCKET_TYPE_ROUTER)) {
+ SILC_LOG_DEBUG(("We are already connected to this router"));
+ continue;
+ }
+
/* Allocate connection object for hold connection specific stuff. */
sconn = silc_calloc(1, sizeof(*sconn));
sconn->server = server;
if (!server->router_conn && !sconn->backup)
server->router_conn = sconn;
- silc_schedule_task_add(server->schedule, fd,
+ silc_schedule_task_add(server->schedule, 0,
silc_server_connect_router,
(void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
SILC_TASK_PRI_NORMAL);
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;
}
/* Announce our clients and channels to the router */
silc_server_announce_clients(server, 0, server->router->connection);
silc_server_announce_channels(server, 0, server->router->connection);
+
+#ifdef BACKUP_SINGLE_ROUTER
+ /* If we are backup router then this primary router is whom we are
+ backing up. */
+ if (server->server_type == SILC_BACKUP_ROUTER)
+ silc_server_backup_add(server, server->id_entry, sock->ip, 0, TRUE);
+#endif /* BACKUP_SINGLE_ROUTER */
}
} else {
/* Add this server to be our backup router */
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. */
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");
+ 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);
/* 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++;
+ 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_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, NULL);
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
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;
}
/* Statistics */
server->stat.my_clients++;
server->stat.clients++;
- if (server->server_type == SILC_ROUTER)
- server->stat.cell_clients++;
+ server->stat.cell_clients++;
/* Get connection parameters */
if (conn->param) {
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;
}
/* Statistics */
- if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER)
+ if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER) {
server->stat.my_servers++;
- else
+ } else {
server->stat.my_routers++;
+ server->stat.routers++;
+ }
server->stat.servers++;
id_entry = (void *)new_server;
/* Check whether this connection is to be our primary router connection
if we do not already have the primary route. */
- if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
+ if (!backup_router &&
+ server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) {
if (silc_server_config_is_primary_route(server) && !initiator)
break;
/* 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;
silc_server_ftp(server, sock, packet);
break;
+ case SILC_PACKET_RESUME_CLIENT:
+ /* Resume client */
+ SILC_LOG_DEBUG(("Resume Client packet"));
+ if (packet->flags & SILC_PACKET_FLAG_LIST)
+ break;
+ silc_server_resume_client(server, sock, packet);
+ break;
+
case SILC_PACKET_RESUME_ROUTER:
/* Resume router packet received. This packet is received for backup
router resuming protocol. */
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;
+ 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);
- /* 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);
+ /* 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);
+
+ /* Remove this client from watcher list if it is */
+ silc_server_del_from_watcher_list(server, client);
+ }
/* Update statistics */
server->stat.my_clients--;
server->stat.clients--;
- if (server->server_type == SILC_ROUTER)
+ if (server->stat.cell_clients)
server->stat.cell_clients--;
SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+ silc_schedule_task_del_by_context(server->schedule, client);
/* We will not delete the client entry right away. We will take it
into history (for WHOWAS command) for 5 minutes */
(void *)i, 300, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+ client->mode = 0;
client->router = NULL;
client->connection = NULL;
- client->mode = 0;
}
/* Frees user_data pointer from socket connection object. This also sends
backup_router->server_name));
SILC_LOG_DEBUG(("New primary router is backup router %s",
backup_router->server_name));
- server->id_entry->router = backup_router;
- server->router = backup_router;
- server->router_connect = time(0);
- server->backup_primary = TRUE;
+#ifdef BACKUP_SINGLE_ROUTER
+ if (server->id_entry != backup_router) {
+#endif /* BACKUP_SINGLE_ROUTER */
+ server->id_entry->router = backup_router;
+ server->router = backup_router;
+ server->router_connect = time(0);
+ server->backup_primary = TRUE;
+#ifdef BACKUP_SINGLE_ROUTER
+ } else {
+ server->id_entry->router = NULL;
+ server->router = NULL;
+ server->standalone = TRUE;
+ }
+#endif /* BACKUP_SINGLE_ROUTER */
+
if (server->server_type == SILC_BACKUP_ROUTER) {
server->server_type = SILC_ROUTER;
silc_idlist_del_data(user_data);
if (!silc_idlist_del_server(server->local_list, user_data))
silc_idlist_del_server(server->global_list, user_data);
- server->stat.my_servers--;
+ if (sock->type == SILC_SOCKET_TYPE_SERVER) {
+ server->stat.my_servers--;
+ } else {
+ server->stat.my_routers--;
+ server->stat.routers--;
+ }
server->stat.servers--;
if (server->server_type == SILC_ROUTER)
server->stat.cell_servers--;
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--;
channel->global_users = FALSE;
silc_free(chl);
- server->stat.my_chanclients--;
+
+ /* Update statistics */
+ if (client->connection)
+ server->stat.my_chanclients--;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.cell_chanclients--;
+ server->stat.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--;
channel->global_users = FALSE;
silc_free(chl);
- server->stat.my_chanclients--;
+
+ /* Update statistics */
+ if (client->connection)
+ server->stat.my_chanclients--;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.cell_chanclients--;
+ server->stat.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");
}
silc_id_get_len(entry->id, SILC_ID_CHANNEL),
entry->mode);
+ /* Distribute to backup routers */
+ if (broadcast && server->server_type == SILC_ROUTER) {
+ SilcBuffer packet;
+ unsigned char *cid;
+ SilcUInt32 name_len = strlen(channel_name);
+ SilcUInt32 channel_id_len = silc_id_get_len(entry->id, SILC_ID_CHANNEL);
+ cid = silc_id_id2str(entry->id, SILC_ID_CHANNEL);
+
+ packet = silc_channel_payload_encode(channel_name, name_len,
+ cid, channel_id_len, entry->mode);
+ silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+ packet->data, packet->len, FALSE, TRUE);
+ silc_free(cid);
+ silc_buffer_free(packet);
+ }
+
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.channels++;
+ server->stat.cell_channels++;
+ entry->users_resolved = TRUE;
+ }
return entry;
}
silc_id_get_len(entry->id, SILC_ID_CHANNEL),
entry->mode);
+ /* Distribute to backup routers */
+ if (broadcast && server->server_type == SILC_ROUTER) {
+ SilcBuffer packet;
+ unsigned char *cid;
+ SilcUInt32 name_len = strlen(channel_name);
+ SilcUInt32 channel_id_len = silc_id_get_len(entry->id, SILC_ID_CHANNEL);
+ cid = silc_id_id2str(entry->id, SILC_ID_CHANNEL);
+
+ packet = silc_channel_payload_encode(channel_name, name_len,
+ cid, channel_id_len, entry->mode);
+ silc_server_backup_send(server, NULL, SILC_PACKET_NEW_CHANNEL, 0,
+ packet->data, packet->len, FALSE, TRUE);
+ silc_free(cid);
+ silc_buffer_free(packet);
+ }
+
server->stat.my_channels++;
+ if (server->server_type == SILC_ROUTER) {
+ server->stat.channels++;
+ server->stat.cell_channels++;
+ entry->users_resolved = TRUE;
+ }
return entry;
}
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);
+ fkey = NULL;
+ fkey_len = 0;
+
+ /* 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);
+ fkey = NULL;
+ fkey_len = 0;
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'. */
SilcClientID *client_id;
SilcClientEntry client;
SilcIDCacheEntry cache;
+ SilcChannelClientEntry chl;
bool global;
SILC_LOG_DEBUG(("Start"));
silc_free(client_id);
- if (!silc_server_client_on_channel(client, channel, NULL)) {
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
/* Client was not on the channel, add it. */
- SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl));
+ chl = silc_calloc(1, sizeof(*chl));
chl->client = client;
chl->mode = mode;
chl->channel = channel;
silc_hash_table_add(channel->user_list, chl->client, chl);
silc_hash_table_add(client->channels, chl->channel, chl);
channel->user_count++;
+ } else {
+ /* Update mode */
+ chl->mode = mode;
+ }
+ }
+}
+
+/* Saves channels and channels user modes to the `client'. Removes
+ the client from those channels that are not sent in the list but
+ it has joined. */
+
+void silc_server_save_user_channels(SilcServer server,
+ SilcSocketConnection sock,
+ SilcClientEntry client,
+ SilcBuffer channels,
+ SilcBuffer channels_user_modes)
+{
+ SilcDList ch;
+ SilcUInt32 *chumodes;
+ SilcChannelPayload entry;
+ SilcChannelEntry channel;
+ SilcChannelID *channel_id;
+ SilcChannelClientEntry chl;
+ SilcHashTable ht = NULL;
+ SilcHashTableList htl;
+ char *name;
+ int i = 0;
+
+ if (!channels ||!channels_user_modes)
+ goto out;
+
+ ch = silc_channel_payload_parse_list(channels->data, channels->len);
+ if (ch && silc_get_mode_list(channels_user_modes, silc_dlist_count(ch),
+ &chumodes)) {
+ ht = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL,
+ NULL, NULL, NULL, TRUE);
+ silc_dlist_start(ch);
+ while ((entry = silc_dlist_get(ch)) != SILC_LIST_END) {
+ /* Check if we have this channel, and add it if we don't have it.
+ Also add the client on the channel unless it is there already. */
+ channel_id = silc_channel_get_id_parse(entry);
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel)
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ if (server->server_type != SILC_SERVER) {
+ silc_free(channel_id);
+ i++;
+ continue;
+ }
+
+ /* We don't have that channel anywhere, add it. */
+ name = silc_channel_get_name(entry, NULL);
+ channel = silc_idlist_add_channel(server->global_list, strdup(name), 0,
+ channel_id, server->router,
+ NULL, NULL, 0);
+ if (!channel) {
+ silc_free(channel_id);
+ i++;
+ continue;
+ }
+ channel_id = NULL;
+ }
+
+ channel->mode = silc_channel_get_mode(entry);
+
+ /* Add the client on the channel */
+ if (!silc_server_client_on_channel(client, channel, &chl)) {
+ chl = silc_calloc(1, sizeof(*chl));
+ chl->client = client;
+ chl->mode = chumodes[i++];
+ chl->channel = channel;
+ silc_hash_table_add(channel->user_list, chl->client, chl);
+ silc_hash_table_add(client->channels, chl->channel, chl);
+ channel->user_count++;
+ } else {
+ /* Update mode */
+ chl->mode = chumodes[i++];
+ }
+
+ silc_hash_table_add(ht, channel, channel);
+ silc_free(channel_id);
+ }
+ silc_channel_payload_list_free(ch);
+ silc_free(chumodes);
+ }
+
+ out:
+ /* Go through the list again and remove client from channels that
+ are no part of the list. */
+ if (ht) {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ if (!silc_hash_table_find(ht, chl->channel, NULL, NULL)) {
+ silc_hash_table_del(chl->channel->user_list, chl->client);
+ silc_hash_table_del(chl->client->channels, chl->channel);
+ silc_free(chl);
+ }
}
+ silc_hash_table_list_reset(&htl);
+ silc_hash_table_free(ht);
+ } else {
+ silc_hash_table_list(client->channels, &htl);
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ silc_hash_table_del(chl->channel->user_list, chl->client);
+ silc_hash_table_del(chl->client->channels, chl->channel);
+ silc_free(chl);
+ }
+ silc_hash_table_list_reset(&htl);
}
}
could not be found to the client. If the `client_id' is specified then
it is used and the `id_data' is ignored. */
-SilcSocketConnection silc_server_get_client_route(SilcServer server,
- unsigned char *id_data,
- SilcUInt32 id_len,
- SilcClientID *client_id,
- SilcIDListData *idata)
+SilcSocketConnection
+silc_server_get_client_route(SilcServer server,
+ unsigned char *id_data,
+ SilcUInt32 id_len,
+ SilcClientID *client_id,
+ SilcIDListData *idata,
+ SilcClientEntry *client_entry)
{
SilcClientID *id;
SilcClientEntry client;
SILC_LOG_DEBUG(("Start"));
+ if (client_entry)
+ *client_entry = NULL;
+
/* Decode destination Client ID */
if (!client_id) {
id = silc_id_str2id(id_data, id_len, SILC_ID_CLIENT);
/* Seems that client really is directly connected to us */
if (idata)
*idata = (SilcIDListData)client;
+ if (client_entry)
+ *client_entry = client;
return client->connection;
}
Secret channels are not put to the list. */
SilcBuffer silc_server_get_client_channel_list(SilcServer server,
- SilcClientEntry client)
+ SilcClientEntry client,
+ bool get_private,
+ bool get_secret,
+ SilcBuffer *user_mode_list)
{
SilcBuffer buffer = NULL;
SilcChannelEntry channel;
SilcUInt16 name_len;
int len;
+ if (user_mode_list)
+ *user_mode_list = NULL;
+
silc_hash_table_list(client->channels, &htl);
while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
channel = chl->channel;
- if (channel->mode & SILC_CHANNEL_MODE_SECRET ||
- channel->mode & SILC_CHANNEL_MODE_PRIVATE)
+ if (channel->mode & SILC_CHANNEL_MODE_SECRET && !get_secret)
+ continue;
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVATE && !get_private)
continue;
cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
len = 4 + name_len + id_len + 4;
buffer = silc_buffer_realloc(buffer,
- (buffer ? (buffer)->truelen + len : len));
- silc_buffer_pull_tail(buffer, ((buffer)->end - (buffer)->data));
+ (buffer ? buffer->truelen + len : len));
+ silc_buffer_pull_tail(buffer, (buffer->end - buffer->data));
silc_buffer_format(buffer,
SILC_STR_UI_SHORT(name_len),
SILC_STR_UI_XNSTRING(channel->channel_name,
name_len),
SILC_STR_UI_SHORT(id_len),
SILC_STR_UI_XNSTRING(cid, id_len),
- SILC_STR_UI_INT(chl->mode), /* Client's mode */
+ SILC_STR_UI_INT(chl->channel->mode),
SILC_STR_END);
silc_buffer_pull(buffer, len);
silc_free(cid);
+
+ if (user_mode_list) {
+ *user_mode_list = silc_buffer_realloc(*user_mode_list,
+ (*user_mode_list ?
+ (*user_mode_list)->truelen + 4 :
+ 4));
+ silc_buffer_pull_tail(*user_mode_list, ((*user_mode_list)->end -
+ (*user_mode_list)->data));
+ SILC_PUT32_MSB(chl->mode, (*user_mode_list)->data);
+ silc_buffer_pull(*user_mode_list, 4);
+ }
}
silc_hash_table_list_reset(&htl);
if (buffer)
silc_buffer_push(buffer, buffer->data - buffer->head);
+ if (user_mode_list && *user_mode_list)
+ silc_buffer_push(*user_mode_list, ((*user_mode_list)->data -
+ (*user_mode_list)->head));
return buffer;
}
SilcClientEntry silc_server_get_client_resolve(SilcServer server,
SilcClientID *client_id,
+ bool always_resolve,
bool *resolved)
{
SilcClientEntry client;
if (!client && server->standalone)
return NULL;
- if (!client || !client->nickname || !client->username) {
+ if (!client || !client->nickname || !client->username ||
+ always_resolve) {
SilcBuffer buffer, idp;
- client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
- client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
- client->resolve_cmd_ident = ++server->cmd_ident;
+ if (client) {
+ client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+ client->resolve_cmd_ident = ++server->cmd_ident;
+ }
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,
silc_ske_free(ctx->ske);
silc_free(ctx);
}
+
+/* Task callback used to retrieve network statistical information from
+ router server once in a while. */
+
+SILC_TASK_CALLBACK(silc_server_get_stats)
+{
+ SilcServer server = (SilcServer)context;
+ SilcBuffer idp, packet;
+
+ SILC_LOG_DEBUG(("Retrieving stats from router"));
+
+ if (!server->standalone) {
+ idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+ packet = silc_command_payload_encode_va(SILC_COMMAND_STATS,
+ ++server->cmd_ident, 1,
+ 1, idp->data, idp->len);
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_COMMAND, 0, packet->data,
+ packet->len, FALSE);
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+ }
+
+ silc_schedule_task_add(server->schedule, 0, silc_server_get_stats,
+ server, 120, 0, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_LOW);
+}