+ /* Initialize ID caches */
+ server->local_list->clients =
+ silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+ server);
+ server->local_list->servers =
+ silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+ server);
+ server->local_list->channels =
+ silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+ NULL);
+
+ /* These are allocated for normal server as well as these hold some
+ global information that the server has fetched from its router. For
+ router these are used as they are supposed to be used on router. */
+ server->global_list->clients =
+ silc_idcache_alloc(0, SILC_ID_CLIENT, silc_idlist_client_destructor,
+ server);
+ server->global_list->servers =
+ silc_idcache_alloc(0, SILC_ID_SERVER, silc_idlist_server_destructor,
+ server);
+ server->global_list->channels =
+ silc_idcache_alloc(0, SILC_ID_CHANNEL, silc_idlist_channel_destructor,
+ NULL);
+
+ /* Init watcher lists */
+ 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;
+ server->watcher_list_pk =
+ silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+ silc_hash_public_key_compare, NULL,
+ NULL, NULL, TRUE);
+ if (!server->watcher_list_pk)
+ goto err;
+
+ /* Create TCP listener */
+ listener = 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);
+ if (!listener)
+ goto err;
+ silc_dlist_add(server->listeners, listener);
+
+ /* Create a Server ID for the server. */
+ port = silc_net_listener_get_port(listener, NULL);
+ ip = silc_net_listener_get_ip(listener, NULL);
+ silc_id_create_server_id(server->config->server_info->primary->public_ip ?
+ server->config->server_info->primary->public_ip :
+ ip[0], port[0], server->rng, &id);
+ if (!id)
+ goto err;
+
+ silc_free(port);
+ silc_free(ip[0]);
+ silc_free(ip);
+
+ server->id = id;
+ server->server_name = server->config->server_info->server_name;
+ server->config->server_info->server_name = NULL;
+ silc_id_id2str(server->id, SILC_ID_SERVER, server->id_string,
+ sizeof(server->id_string), &server->id_string_len);
+
+ /* Add ourselves to the server list. We don't have a router yet
+ beacuse we haven't established a route yet. It will be done later.
+ For now, NULL is sent as router. This allocates new entry to
+ the ID list. */
+ id_entry =
+ silc_idlist_add_server(server->local_list, strdup(server->server_name),
+ server->server_type,
+ silc_id_dup(server->id, SILC_ID_SERVER),
+ NULL, NULL);
+ if (!id_entry) {
+ SILC_LOG_ERROR(("Could not add local server to cache"));
+ goto err;
+ }
+ id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ id_entry->data.conn_type = (server->server_type == SILC_SERVER ?
+ SILC_CONN_SERVER : SILC_CONN_ROUTER);
+ server->id_entry = id_entry;
+
+ /* Create secondary TCP listeners */
+ if (silc_server_init_secondary(server) == FALSE)
+ goto err;
+
+ server->listenning = TRUE;
+
+ /* Create connections to configured routers. */
+ silc_server_create_connections(server);
+
+ /* If server connections has been configured then we must be router as
+ normal server cannot have server connections, only router connections. */
+ if (server->config->servers) {
+ SilcServerConfigServer *ptr = server->config->servers;
+
+ 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;
+ }
+ }
+
+ if (server->server_type != SILC_ROUTER) {
+ server->stat.servers = 1;
+ server->stat.cell_servers = 1;
+ } else {
+ server->stat.routers = 1;
+ }
+
+ /* If we are normal server we'll retrieve network statisticial information
+ once in a while from the router. */
+ if (server->server_type != SILC_ROUTER)
+ silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats,
+ server, 10, 0);
+
+ /* Start packet engine */
+ server->packet_engine =
+ silc_packet_engine_start(server->rng, server->server_type == SILC_ROUTER,
+ &silc_server_stream_cbs, server);
+ if (!server->packet_engine)
+ goto err;
+
+ /* Register client entry expiration timeout */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_purge_expired_clients, server,
+ 120, 0);
+
+ /* Initialize HTTP server */
+ silc_server_http_init(server);
+
+ SILC_LOG_DEBUG(("Server initialized"));
+
+ /* We are done here, return succesfully */
+ return TRUE;
+
+ err:
+ silc_server_config_unref(&server->config_ref);
+ return FALSE;
+}
+
+/* Task callback to close a socket connection after rehash */
+
+SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
+{
+ SilcServer server = app_context;
+ SilcPacketStream sock = context;
+ SilcIDListData idata = silc_packet_get_context(sock);
+ const char *hostname;
+ SilcUInt16 port;
+
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, &hostname, NULL, &port);
+
+ SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured",
+ hostname, port, SILC_CONNTYPE_STRING(idata->conn_type)));
+ silc_schedule_task_del_by_context(server->schedule, sock);
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_BANNED_FROM_SERVER,
+ "This connection is removed from "
+ "configuration");
+ silc_server_free_sock_user_data(server, sock, NULL);
+}
+
+/* This function basically reads the config file again and switches the config
+ object pointed by the server object. After that, we have to fix various
+ things such as the server_name and the listening ports.
+ Keep in mind that we no longer have the root privileges at this point. */
+
+SilcBool silc_server_rehash(SilcServer server)
+{
+ SilcServerConfig newconfig;
+
+ 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) */
+ newconfig = silc_server_config_alloc(server->config_file, server);
+ if (!newconfig) {
+ SILC_LOG_ERROR(("Rehash FAILED."));
+ return FALSE;
+ }
+
+ /* Fix the server_name field */
+ if (strcmp(server->server_name, newconfig->server_info->server_name)) {
+ silc_free(server->server_name);
+
+ /* Check server name */
+ server->server_name =
+ silc_identifier_check(newconfig->server_info->server_name,
+ strlen(newconfig->server_info->server_name),
+ SILC_STRING_LOCALE, 256, NULL);
+ if (!server->server_name) {
+ SILC_LOG_ERROR(("Malformed server name string '%s'",
+ server->config->server_info->server_name));
+ return FALSE;
+ }
+
+ /* 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_update_by_context(server->local_list->servers,
+ server->id_entry, NULL,
+ strdup(server->id_entry->server_name),
+ TRUE);
+ }
+
+ /* Set logging */
+ silc_server_config_setlogfiles(server);
+
+ /* Change new key pair if necessary */
+ if (newconfig->server_info->public_key &&
+ !silc_pkcs_public_key_compare(server->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 = 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;
+ }
+
+ /* Check for unconfigured server and router connections and close
+ connections that were unconfigured. */
+
+ if (server->config->routers) {
+ SilcServerConfigRouter *ptr;
+ SilcServerConfigRouter *newptr;
+ SilcBool found;
+
+ for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+ found = FALSE;
+
+ /* Check whether new config has this one too */
+ for (newptr = newconfig->routers; newptr; newptr = newptr->next) {
+ if (silc_string_compare(newptr->host, ptr->host) &&
+ newptr->port == ptr->port &&
+ newptr->initiator == ptr->initiator) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found && ptr->host) {
+ /* Remove this connection */
+ SilcPacketStream sock;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_ROUTER,
+ ptr->host, ptr->port);
+ if (sock)
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_rehash_close_connection,
+ sock, 0, 1);
+ }
+ }
+ }
+
+ if (server->config->servers) {
+ SilcServerConfigServer *ptr;
+ SilcServerConfigServer *newptr;
+ SilcBool found;
+
+ for (ptr = server->config->servers; ptr; ptr = ptr->next) {
+ found = FALSE;
+
+ /* Check whether new config has this one too */
+ for (newptr = newconfig->servers; newptr; newptr = newptr->next) {
+ if (silc_string_compare(newptr->host, ptr->host)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found && ptr->host) {
+ /* Remove this connection */
+ SilcPacketStream sock;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_SERVER,
+ ptr->host, 0);
+ if (sock)
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_rehash_close_connection,
+ sock, 0, 1);
+ }
+ }
+ }
+
+ if (server->config->clients) {
+ SilcServerConfigClient *ptr;
+ SilcServerConfigClient *newptr;
+ SilcBool found;
+
+ for (ptr = server->config->clients; ptr; ptr = ptr->next) {
+ found = FALSE;
+
+ /* Check whether new config has this one too */
+ for (newptr = newconfig->clients; newptr; newptr = newptr->next) {
+ if (silc_string_compare(newptr->host, ptr->host)) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found && ptr->host) {
+ /* Remove this connection */
+ SilcPacketStream sock;
+ sock = silc_server_find_socket_by_host(server, SILC_CONN_CLIENT,
+ ptr->host, 0);
+ if (sock)
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_rehash_close_connection,
+ sock, 0, 1);
+ }
+ }
+ }
+
+ /* Create connections after rehash */
+ silc_server_create_connections(server);
+
+ /* Check whether our router status has changed */
+ if (newconfig->servers) {
+ SilcServerConfigServer *ptr = newconfig->servers;
+
+ 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;
+ }
+ }
+
+ /* 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);
+
+ /* Take new config context */
+ server->config = newconfig;
+ silc_server_config_ref(&server->config_ref, server->config, server->config);
+
+#ifdef SILC_DEBUG
+ /* Set debugging on if configured */
+ if (server->config->debug_string) {
+ silc_log_debug(TRUE);
+ silc_log_set_debug_string(server->config->debug_string);
+ }
+#endif /* SILC_DEBUG */
+
+ SILC_LOG_DEBUG(("Server rehashed"));
+
+ return TRUE;
+}
+
+/* The heart of the server. This runs the scheduler thus runs the server.
+ When this returns the server has been stopped and the program will
+ be terminated. */
+
+void silc_server_run(SilcServer server)
+{
+ SILC_LOG_INFO(("SILC Server started"));
+
+ /* Start the scheduler, the heart of the SILC server. When this returns
+ the program will be terminated. */
+ silc_schedule(server->schedule);
+}
+
+/* Stops the SILC server. This function is used to shutdown the server.
+ This is usually called after the scheduler has returned. After stopping
+ the server one should call silc_server_free. */
+
+void silc_server_stop(SilcServer server)
+{
+ SilcDList list;
+ SilcPacketStream ps;
+ SilcNetListener listener;
+
+ SILC_LOG_INFO(("SILC Server shutting down"));
+
+ server->server_shutdown = TRUE;
+
+ /* Close all connections */
+ if (server->packet_engine) {
+ list = silc_packet_engine_get_streams(server->packet_engine);
+
+ silc_dlist_start(list);
+ while ((ps = silc_dlist_get(list))) {
+ SilcIDListData idata = silc_packet_get_context(ps);
+
+ if (!silc_packet_stream_is_valid(ps))
+ continue;
+
+ if (idata)
+ idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
+
+ silc_server_disconnect_remote(server, ps, SILC_STATUS_OK,
+ "Server is shutting down");
+ silc_server_free_sock_user_data(server, ps,
+ "Server is shutting down");
+ }
+ silc_packet_engine_free_streams_list(list);
+ }
+
+ /* We are not connected to network anymore */
+ server->standalone = TRUE;
+
+ silc_dlist_start(server->listeners);
+ while ((listener = silc_dlist_get(server->listeners)))
+ silc_net_close_listener(listener);
+
+ silc_server_http_uninit(server);
+
+ /* 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);
+ silc_schedule_task_del_by_callback(server->schedule,
+ silc_server_connect_to_router);
+
+ silc_schedule_stop(server->schedule);
+
+ SILC_LOG_DEBUG(("Server stopped"));
+}
+
+/* Purge expired client entries from the server */
+
+SILC_TASK_CALLBACK(silc_server_purge_expired_clients)
+{
+ SilcServer server = context;
+ SilcClientEntry client;
+ SilcIDList id_list;
+ SilcUInt64 curtime = silc_time();
+
+ SILC_LOG_DEBUG(("Expire timeout"));
+
+ silc_dlist_start(server->expired_clients);
+ while ((client = silc_dlist_get(server->expired_clients))) {
+ /* For unregistered clients the created timestamp is actually
+ unregistered timestamp. Make sure client remains in history
+ at least 500 seconds. */
+ if (client->data.created && curtime - client->data.created < 500)
+ continue;
+
+ id_list = (client->data.status & SILC_IDLIST_STATUS_LOCAL ?
+ server->local_list : server->global_list);
+
+ silc_idlist_del_data(client);
+ silc_idlist_del_client(id_list, client);
+ silc_dlist_del(server->expired_clients, client);
+ }
+
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_purge_expired_clients, server,
+ 120, 0);
+}
+
+
+/******************************* Connecting *********************************/
+
+/* Free connection context */
+
+void silc_server_connection_free(SilcServerConnection sconn)
+{
+ if (!sconn)
+ return;
+ SILC_LOG_DEBUG(("Free connection %p", sconn));
+ silc_dlist_del(sconn->server->conns, sconn);
+ silc_server_config_unref(&sconn->conn);
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
+}
+
+/* Creates connection to a remote router. */
+
+void silc_server_create_connection(SilcServer server,
+ SilcBool reconnect,
+ SilcBool dynamic,
+ const char *remote_host, SilcUInt32 port,
+ SilcServerConnectCallback callback,
+ void *context)
+{
+ SilcServerConnection sconn;
+
+ /* Allocate connection object for hold connection specific stuff. */
+ sconn = silc_calloc(1, sizeof(*sconn));
+ if (!sconn)