+ 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)
+ return;
+ sconn->remote_host = strdup(remote_host);
+ sconn->remote_port = port;
+ sconn->no_reconnect = reconnect == FALSE;
+ sconn->callback = callback;
+ sconn->callback_context = context;
+ sconn->no_conf = dynamic;
+ sconn->server = server;
+
+ SILC_LOG_DEBUG(("Created connection %p", sconn));
+
+ silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
+ sconn, 0, 0);
+}
+
+/* Connection authentication completion callback */
+
+static void
+silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success,
+ void *context)
+{
+ SilcServerConnection sconn = context;
+ SilcUnknownEntry entry = silc_packet_get_context(sconn->sock);
+ SilcServer server = entry->server;
+ SilcServerConfigServer *conn;
+ SilcServerConfigConnParams *param;
+ SilcIDListData idata;
+ SilcServerEntry id_entry = NULL;
+ unsigned char id[32];
+ SilcUInt32 id_len;
+ SilcID remote_id;
+ const char *ip;
+
+ SILC_LOG_DEBUG(("Connection %p authentication completed, entry %p",
+ sconn, entry));
+
+ entry->op = NULL;
+
+ if (success == FALSE) {
+ /* Authentication failed */
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);
+ return;
+ }
+
+ /* XXX For now remote is router always */
+ entry->data.conn_type = SILC_CONN_ROUTER;
+
+ SILC_LOG_INFO(("Connected to %s %s",
+ SILC_CONNTYPE_STRING(entry->data.conn_type),
+ sconn->remote_host));
+
+ /* Create the actual entry for remote entity */
+ switch (entry->data.conn_type) {
+ case SILC_CONN_SERVER:
+ SILC_LOG_DEBUG(("Remote is SILC server"));
+
+ /* Add new server. The server must register itself to us before it
+ becomes registered to SILC network. */
+ id_entry = silc_idlist_add_server(server->local_list,
+ strdup(sconn->remote_host),
+ SILC_SERVER, NULL, NULL, sconn->sock);
+ if (!id_entry) {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+
+ /* Statistics */
+ server->stat.my_servers++;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.servers++;
+ SILC_LOG_DEBUG(("my_servers %d", server->stat.my_servers));
+
+ silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+ break;
+
+ case SILC_CONN_ROUTER:
+ SILC_LOG_DEBUG(("Remote is SILC router"));
+
+ /* Register to network */
+ silc_id_id2str(server->id, SILC_ID_SERVER, id, sizeof(id), &id_len);
+ if (!silc_packet_send_va(sconn->sock, SILC_PACKET_NEW_SERVER, 0,
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_DATA(id, id_len),
+ SILC_STR_UI_SHORT(strlen(server->server_name)),
+ SILC_STR_DATA(server->server_name,
+ strlen(server->server_name)),
+ SILC_STR_END)) {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+
+ /* Get remote ID */
+ silc_packet_get_ids(sconn->sock, NULL, NULL, NULL, &remote_id);
+
+ /* Check that we do not have this ID already */
+ id_entry = silc_idlist_find_server_by_id(server->local_list,
+ &remote_id.u.server_id,
+ TRUE, NULL);
+ if (id_entry) {
+ silc_idcache_del_by_context(server->local_list->servers, id_entry, NULL);
+ } else {
+ id_entry = silc_idlist_find_server_by_id(server->global_list,
+ &remote_id.u.server_id,
+ TRUE, NULL);
+ if (id_entry)
+ silc_idcache_del_by_context(server->global_list->servers, id_entry,
+ NULL);
+ }
+
+ SILC_LOG_DEBUG(("New server id(%s)",
+ silc_id_render(&remote_id.u.server_id, SILC_ID_SERVER)));
+
+ /* Add the connected router to global server list. Router is sent
+ as NULL since it's local to us. */
+ id_entry = silc_idlist_add_server(server->global_list,
+ strdup(sconn->remote_host),
+ SILC_ROUTER,
+ silc_id_dup(&remote_id.u.server_id,
+ SILC_ID_SERVER),
+ NULL, sconn->sock);
+ if (!id_entry) {
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+
+ /* Registered */
+ silc_idlist_add_data(id_entry, (SilcIDListData)entry);
+ idata = (SilcIDListData)id_entry;
+ idata->status |= (SILC_IDLIST_STATUS_REGISTERED |
+ SILC_IDLIST_STATUS_LOCAL);
+ idata->sconn = sconn;
+
+ /* Statistics */
+ server->stat.my_routers++;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.routers++;
+ SILC_LOG_DEBUG(("my_routers %d", server->stat.my_routers));
+
+ if (!sconn->backup) {
+ /* Mark this router our primary router if we're still standalone */
+ if (server->standalone) {
+ SILC_LOG_DEBUG(("This connection is our primary router"));
+ server->id_entry->router = id_entry;
+ server->router = id_entry;
+ server->router->server_type = SILC_ROUTER;
+ server->standalone = FALSE;
+ server->backup_primary = FALSE;
+
+ /* Announce data if we are not backup router (unless not as primary
+ currently). Backup router announces later at the end of
+ resuming protocol. */
+ if (server->backup_router && server->server_type == SILC_ROUTER) {
+ SILC_LOG_DEBUG(("Announce data after resume protocol"));
+ } else {
+ /* If we are router then announce our possible servers. Backup
+ router announces also global servers. */
+ if (server->server_type == SILC_ROUTER)
+ silc_server_announce_servers(server,
+ server->backup_router ? TRUE : FALSE,
+ 0, SILC_PRIMARY_ROUTE(server));
+
+ /* Announce our clients and channels to the router */
+ silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server));
+ silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server));
+ }
+
+ /* If we are backup router then this primary router is whom we are
+ backing up. */
+ if (server->server_type == SILC_BACKUP_ROUTER) {
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sconn->
+ sock),
+ NULL, NULL, &ip, NULL);
+ silc_server_backup_add(server, server->id_entry, ip,
+ sconn->remote_port, TRUE);
+ }
+ }
+#if 0
+ else {
+ /* We already have primary router. Disconnect this connection */
+ SILC_LOG_DEBUG(("We already have primary router, disconnect"));
+ silc_idlist_del_server(server->global_list, id_entry);
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
+ return;
+ }
+#endif /* 0 */
+ } else {
+ /* Add this server to be our backup router */
+ id_entry->server_type = SILC_BACKUP_ROUTER;
+ silc_server_backup_add(server, id_entry, sconn->backup_replace_ip,
+ sconn->backup_replace_port, FALSE);
+ }
+
+ break;
+
+ default:
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_free_sock_user_data(server, sconn->sock, NULL);
+ silc_server_disconnect_remote(server, sconn->sock,
+ SILC_STATUS_ERR_AUTH_FAILED, NULL);