-/* 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)
-{
- SILC_LOG_INFO(("SILC Server shutting down"));
-
- if (server->schedule) {
- int i;
-
- server->server_shutdown = TRUE;
-
- /* Close all connections */
- for (i = 0; i < server->config->param.connections_max; i++) {
- if (!server->sockets[i])
- continue;
- if (!SILC_IS_LISTENER(server->sockets[i])) {
- SilcSocketConnection sock = server->sockets[i];
- SilcIDListData idata = sock->user_data;
-
- if (idata)
- idata->status &= ~SILC_IDLIST_STATUS_DISABLED;
-
- silc_schedule_task_del_by_context(server->schedule,
- server->sockets[i]);
- silc_schedule_task_del_by_fd(server->schedule,
- server->sockets[i]->sock);
- silc_server_disconnect_remote(server, server->sockets[i],
- SILC_STATUS_OK,
- "Server is shutting down");
- if (server->sockets[i]) {
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock,
- "Server is shutting down");
- silc_socket_free(sock);
- }
- } else {
- silc_socket_free(server->sockets[i]);
- server->sockets[i] = NULL;
- }
- }
-
- /* We are not connected to network anymore */
- server->standalone = TRUE;
-
- silc_schedule_stop(server->schedule);
- silc_schedule_uninit(server->schedule);
- server->schedule = NULL;
-
- silc_free(server->sockets);
- server->sockets = NULL;
- }
-
- silc_server_protocols_unregister();
-
- SILC_LOG_DEBUG(("Server stopped"));
-}
-
-/* Function that is called when the network connection to a router has
- been established. This will continue with the key exchange protocol
- with the remote router. */
-
-void silc_server_start_key_exchange(SilcServer server,
- SilcServerConnection sconn,
- int sock)
-{
- SilcSocketConnection newsocket;
- SilcProtocol protocol;
- SilcServerKEInternalContext *proto_ctx;
- SilcServerConfigRouter *conn =
- (SilcServerConfigRouter *) sconn->conn.ref_ptr;
- void *context;
-
- /* Cancel any possible retry timeouts */
- silc_schedule_task_del_by_callback(server->schedule,
- silc_server_connect_to_router_retry);
-
- /* Set socket options */
- silc_net_set_socket_nonblock(sock);
- silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
- /* Create socket connection for the connection. Even though we
- know that we are connecting to a router we will mark the socket
- to be unknown connection until we have executed authentication
- protocol. */
- silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
- server->sockets[sock] = newsocket;
- newsocket->hostname = strdup(sconn->remote_host);
- newsocket->ip = strdup(sconn->remote_host);
- newsocket->port = sconn->remote_port;
- sconn->sock = newsocket;
-
- /* Allocate internal protocol context. This is sent as context
- to the protocol. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->server = (void *)server;
- proto_ctx->context = (void *)sconn;
- proto_ctx->sock = newsocket;
- proto_ctx->rng = server->rng;
- proto_ctx->responder = FALSE;
-
- /* Set Key Exchange flags from configuration, but fall back to global
- settings too. */
- SILC_GET_SKE_FLAGS(conn, proto_ctx);
- if (server->config->param.key_exchange_pfs)
- proto_ctx->flags |= SILC_SKE_SP_FLAG_PFS;
-
- /* Perform key exchange protocol. silc_server_connect_to_router_second
- will be called after the protocol is finished. */
- silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE,
- &protocol, proto_ctx,
- silc_server_connect_to_router_second);
- newsocket->protocol = protocol;
-
- /* Register a timeout task that will be executed if the protocol
- is not executed within set limit. */
- proto_ctx->timeout_task =
- silc_schedule_task_add(server->schedule, sock,
- silc_server_timeout_remote,
- server, server->config->key_exchange_timeout, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
-
- /* Register the connection for network input and output. This sets
- that scheduler will listen for incoming packets for this connection
- and sets that outgoing packets may be sent to this connection as
- well. However, this doesn't set the scheduler for outgoing traffic,
- it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
- later when outgoing data is available. */
- context = (void *)server;
- SILC_REGISTER_CONNECTION_FOR_IO(sock);
-
- /* Run the protocol */
- silc_protocol_execute(protocol, server->schedule, 0, 0);
-}
-
-/* Timeout callback that will be called to retry connecting to remote
- router. This is used by both normal and router server. This will wait
- before retrying the connecting. The timeout is generated by exponential
- backoff algorithm. */
-
-SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
-{
- SilcServer server = app_context;
- SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
- SilcServerConfigConnParams *param =
- (conn->param ? conn->param : &server->config->param);
-
- /* Don't retry if we are shutting down. */
- if (server->server_shutdown) {
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
- return;
- }
-
- SILC_LOG_INFO(("Retrying connecting to a router"));
-
- /* Calculate next timeout */
- if (sconn->retry_count >= 1) {
- sconn->retry_timeout = sconn->retry_timeout * SILC_SERVER_RETRY_MULTIPLIER;
- if (sconn->retry_timeout > param->reconnect_interval_max)
- sconn->retry_timeout = param->reconnect_interval_max;
- } else {
- sconn->retry_timeout = param->reconnect_interval;
- }
- sconn->retry_count++;
- sconn->retry_timeout = sconn->retry_timeout +
- silc_rng_get_rn32(server->rng) % SILC_SERVER_RETRY_RANDOMIZER;
-
- /* If we've reached max retry count, give up. */
- if ((sconn->retry_count > param->reconnect_count) &&
- !param->reconnect_keep_trying) {
- SILC_LOG_ERROR(("Could not connect to router, giving up"));
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
- return;
- }
-
- SILC_LOG_DEBUG(("Retrying connecting to a router in %d seconds",
- sconn->retry_timeout));
-
- /* We will lookup a fresh pointer later */
- silc_server_config_unref(&sconn->conn);
-
- /* Wait one before retrying */
- silc_schedule_task_add(server->schedule, 0, silc_server_connect_router,
- context, sconn->retry_timeout, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-}
-
-/* Generic routine to use connect to a router. */
-
-SILC_TASK_CALLBACK(silc_server_connect_router)
-{
- SilcServer server = app_context;
- SilcServerConnection sconn = (SilcServerConnection)context;
- SilcServerConfigRouter *rconn;
- int sock;
-
- /* Don't connect if we are shutting down. */
- if (server->server_shutdown) {
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
- return;
- }
-
- SILC_LOG_INFO(("Connecting to the %s %s on port %d",
- (sconn->backup ? "backup router" : "router"),
- sconn->remote_host, sconn->remote_port));
-
- server->router_connect = time(NULL);
- rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
- sconn->remote_port);
- if (!rconn) {
- SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
- (sconn->backup ? "backup router" : "router"),
- sconn->remote_host, sconn->remote_port));
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
- return;
- }
- silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
-
- /* Connect to 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));
- if (!sconn->no_reconnect)
- silc_schedule_task_add(server->schedule, 0,
- silc_server_connect_to_router_retry,
- context, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- else {
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
- }
- return;
- }
-
- /* Continue with key exchange protocol */
- silc_server_start_key_exchange(server, sconn, sock);
-}
-
-/* This function connects to our primary router or if we are a router this
- establishes all our primary routes. This is called at the start of the
- server to do authentication and key exchange with our router - called
- from schedule. */
-
-SILC_TASK_CALLBACK_GLOBAL(silc_server_connect_to_router)
-{
- SilcServer server = (SilcServer)context;
- SilcServerConnection sconn;
- SilcServerConfigRouter *ptr;
-
- /* Don't connect if we are shutting down. */
- if (server->server_shutdown)
- return;
-
- SILC_LOG_DEBUG(("We are %s",
- (server->server_type == SILC_SERVER ?
- "normal server" : server->server_type == SILC_ROUTER ?
- "router" : "backup router/normal server")));
-
- if (!server->config->routers) {
- /* There wasn't a configured router, we will continue but we don't
- have a connection to outside world. We will be standalone server. */
- SILC_LOG_DEBUG(("No router(s), we are standalone"));
- server->standalone = TRUE;
- 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) {
-
- SILC_LOG_DEBUG(("%s connection [%s] %s:%d",
- ptr->backup_router ? "Backup router" : "Router",
- ptr->initiator ? "Initiator" : "Responder",
- ptr->host, ptr->port));
-
- if (server->server_type == SILC_ROUTER && ptr->backup_router &&
- ptr->initiator == FALSE && !server->backup_router &&
- !silc_server_config_get_backup_router(server))
- server->wait_backup = TRUE;
-
- if (ptr->initiator) {
- /* Check whether we are connecting or 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"));
-
- /* If we don't have primary router and this connection is our
- primary router we are in desync. Reconnect to the primary. */
- if (server->standalone && !server->router) {
- SilcServerConfigRouter *primary =
- silc_server_config_get_primary_router(server);
- if (primary == ptr) {
- SilcSocketConnection sock =
- silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
- ptr->host, ptr->port);
- if (sock) {
- server->backup_noswitch = TRUE;
- if (sock->user_data)
- silc_server_free_sock_user_data(server, sock, NULL);
- silc_server_disconnect_remote(server, sock, 0, NULL);
- server->backup_noswitch = FALSE;
- SILC_LOG_DEBUG(("Reconnecting to primary router"));
- } else {
- continue;
- }
- } else {
- continue;
- }
- } else {
- continue;
- }
- }
- 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_UNKNOWN)) {
- SILC_LOG_DEBUG(("We are already connecting to this router"));
- continue;
- }
-
- /* Allocate connection object for hold connection specific stuff. */
- sconn = silc_calloc(1, sizeof(*sconn));
- sconn->remote_host = strdup(ptr->host);
- sconn->remote_port = ptr->port;
- sconn->backup = ptr->backup_router;
- if (sconn->backup) {
- sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
- sconn->backup_replace_port = ptr->backup_replace_port;
- }
-
- if (!server->router_conn && !sconn->backup)
- server->router_conn = sconn;
-
- silc_schedule_task_add(server->schedule, 0,
- silc_server_connect_router,
- (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- }
- }
-}
-
-/* Second part of connecting to router(s). Key exchange protocol has been
- executed and now we will execute authentication protocol. */
-
-SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
-{
- SilcProtocol protocol = (SilcProtocol)context;
- SilcServerKEInternalContext *ctx =
- (SilcServerKEInternalContext *)protocol->context;
- SilcServer server = (SilcServer)ctx->server;
- SilcServerConnection sconn = (SilcServerConnection)ctx->context;
- SilcSocketConnection sock = ctx->sock;
- SilcServerConnAuthInternalContext *proto_ctx;
- SilcServerConfigRouter *conn = NULL;
-
- SILC_LOG_DEBUG(("Start"));
-
- if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
- protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
- /* Error occured during protocol */
- silc_protocol_free(protocol);
- sock->protocol = NULL;
- silc_ske_free_key_material(ctx->keymat);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_server_disconnect_remote(server, sock,
- SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
-
- /* Try reconnecting if configuration wants it */
- if (!sconn->no_reconnect) {
- silc_schedule_task_add(server->schedule, 0,
- silc_server_connect_to_router_retry,
- sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- return;
- }
-
- /* Call completion to indicate error */
- if (sconn->callback)
- (*sconn->callback)(server, NULL, sconn->callback_context);
-
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- 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(server, ctx->ske,
- ctx->sock, ctx->keymat,
- ctx->ske->prop->cipher,
- ctx->ske->prop->pkcs,
- ctx->ske->prop->hash,
- ctx->ske->prop->hmac,
- ctx->ske->prop->group,
- ctx->responder)) {
- silc_protocol_free(protocol);
- sock->protocol = NULL;
- silc_ske_free_key_material(ctx->keymat);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_server_disconnect_remote(server, sock,
- SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
-
- /* Try reconnecting if configuration wants it */
- if (!sconn->no_reconnect) {
- silc_schedule_task_add(server->schedule, 0,
- silc_server_connect_to_router_retry,
- sconn, 0, 1, SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
- return;
- }
-
- /* Call completion to indicate error */
- if (sconn->callback)
- (*sconn->callback)(server, NULL, sconn->callback_context);
-
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
- return;
- }
- silc_ske_free_key_material(ctx->keymat);
-
- /* Allocate internal context for the authentication protocol. This
- is sent as context for the protocol. */
- proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
- proto_ctx->server = (void *)server;
- proto_ctx->context = (void *)sconn;
- proto_ctx->sock = sock;
- proto_ctx->ske = ctx->ske; /* Save SKE object from previous protocol */
- proto_ctx->dest_id_type = ctx->dest_id_type;
- proto_ctx->dest_id = ctx->dest_id;
-
- /* Resolve the authentication method used in this connection. Check if
- we find a match from user configured connections */
- if (!sconn->conn.ref_ptr)
- conn = silc_server_config_find_router_conn(server, sock->hostname,
- sock->port);
- else
- conn = sconn->conn.ref_ptr;
-
- if (conn) {
- /* Match found. Use the configured authentication method. Take only
- the passphrase, since for public key auth we automatically use
- our local key pair. */
- if (conn->passphrase) {
- if (conn->publickeys && !server->config->prefer_passphrase_auth) {
- proto_ctx->auth_meth = SILC_AUTH_PUBLIC_KEY;
- } else {
- proto_ctx->auth_data = strdup(conn->passphrase);
- proto_ctx->auth_data_len = strlen(conn->passphrase);
- proto_ctx->auth_meth = SILC_AUTH_PASSWORD;
- }
- } else if (conn->publickeys) {
- proto_ctx->auth_meth = SILC_AUTH_PUBLIC_KEY;
- } else {
- proto_ctx->auth_meth = SILC_AUTH_NONE;
- }
- } else {
- SILC_LOG_ERROR(("Could not find connection data for %s (%s) on port",
- sock->hostname, sock->ip, sock->port));
- silc_protocol_free(protocol);
- sock->protocol = NULL;
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- if (ctx->ske)
- silc_ske_free(ctx->ske);
- silc_free(ctx->dest_id);
- silc_free(ctx);
- silc_server_config_unref(&sconn->conn);
- silc_free(sconn->remote_host);
- silc_free(sconn->backup_replace_ip);
- silc_free(sconn);
- silc_server_disconnect_remote(server, sock,
- SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
- return;
- }
-
- /* Free old protocol as it is finished now */
- silc_protocol_free(protocol);
- if (ctx->packet)
- silc_packet_context_free(ctx->packet);
- silc_free(ctx);
- sock->protocol = NULL;
-
- /* Allocate the authentication protocol. This is allocated here
- but we won't start it yet. We will be receiving party of this
- protocol thus we will wait that connecting party will make
- their first move. */
- silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH,
- &sock->protocol, proto_ctx,
- silc_server_connect_to_router_final);
-
- /* Register timeout task. If the protocol is not executed inside
- this timelimit the connection will be terminated. */
- proto_ctx->timeout_task =
- silc_schedule_task_add(server->schedule, sock->sock,
- silc_server_timeout_remote,
- (void *)server,
- server->config->conn_auth_timeout, 0,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_LOW);
-
- /* Run the protocol */
- silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
-}
-
-/* Finalizes the connection to router. Registers a server task to the
- queue so that we can accept new connections. */