X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=apps%2Fsilcd%2Fserver.c;h=963746f9045acc439a214a8294b17724b529f0cd;hb=413da0f8686910f5e627393157566ae729ca99c4;hp=0f3e2da518a05afdb0a171c576690dddf8a98066;hpb=8221f7af775a38780ffacfd876b68a2edda8eb97;p=silc.git diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 0f3e2da5..963746f9 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -4,12 +4,11 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2002 Pekka Riikonen + Copyright (C) 1997 - 2003 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. + the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,9 +27,9 @@ #include "server_internal.h" /* Static prototypes */ +SILC_TASK_CALLBACK(silc_server_rehash_close_connection); SILC_TASK_CALLBACK(silc_server_connect_to_router_retry); SILC_TASK_CALLBACK(silc_server_connect_router); -SILC_TASK_CALLBACK(silc_server_connect_to_router); SILC_TASK_CALLBACK(silc_server_connect_to_router_second); SILC_TASK_CALLBACK(silc_server_connect_to_router_final); SILC_TASK_CALLBACK(silc_server_accept_new_connection); @@ -42,8 +41,6 @@ SILC_TASK_CALLBACK(silc_server_close_connection_final); SILC_TASK_CALLBACK(silc_server_free_client_data_timeout); SILC_TASK_CALLBACK(silc_server_timeout_remote); 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 @@ -77,21 +74,26 @@ int silc_server_alloc(SilcServer *new_server) void silc_server_free(SilcServer server) { + SilcIDCacheList list; + SilcIDCacheEntry cache; + if (!server) return; #ifdef SILC_SIM { SilcSim sim; - + silc_dlist_start(server->sim); while ((sim = silc_dlist_get(server->sim)) != SILC_LIST_END) { silc_dlist_del(server->sim, sim); + silc_sim_close(sim); silc_sim_free(sim); } silc_dlist_uninit(server->sim); } #endif + silc_server_backup_free(server); silc_server_config_unref(&server->config_ref); if (server->rng) silc_rng_free(server->rng); @@ -106,6 +108,66 @@ void silc_server_free(SilcServer server) if (server->id_entry) silc_idlist_del_server(server->local_list, server->id_entry); + /* Delete all channels */ + list = NULL; + if (silc_idcache_get_all(server->local_list->channels, &list) && + silc_idcache_list_first(list, &cache)) { + silc_idlist_del_channel(server->local_list, cache->context); + while (silc_idcache_list_next(list, &cache)) + silc_idlist_del_channel(server->local_list, cache->context); + } + if (list) + silc_idcache_list_free(list); + list = NULL; + if (silc_idcache_get_all(server->global_list->channels, &list) && + silc_idcache_list_first(list, &cache)) { + silc_idlist_del_channel(server->global_list, cache->context); + while (silc_idcache_list_next(list, &cache)) + silc_idlist_del_channel(server->global_list, cache->context); + } + if (list) + silc_idcache_list_free(list); + + /* Delete all clients */ + list = NULL; + if (silc_idcache_get_all(server->local_list->clients, &list) && + silc_idcache_list_first(list, &cache)) { + silc_idlist_del_client(server->local_list, cache->context); + while (silc_idcache_list_next(list, &cache)) + silc_idlist_del_client(server->local_list, cache->context); + } + if (list) + silc_idcache_list_free(list); + list = NULL; + if (silc_idcache_get_all(server->global_list->clients, &list) && + silc_idcache_list_first(list, &cache)) { + silc_idlist_del_client(server->global_list, cache->context); + while (silc_idcache_list_next(list, &cache)) + silc_idlist_del_client(server->global_list, cache->context); + } + if (list) + silc_idcache_list_free(list); + + /* Delete all servers */ + list = NULL; + if (silc_idcache_get_all(server->local_list->servers, &list) && + silc_idcache_list_first(list, &cache)) { + silc_idlist_del_server(server->local_list, cache->context); + while (silc_idcache_list_next(list, &cache)) + silc_idlist_del_server(server->local_list, cache->context); + } + if (list) + silc_idcache_list_free(list); + list = NULL; + if (silc_idcache_get_all(server->global_list->servers, &list) && + silc_idcache_list_first(list, &cache)) { + silc_idlist_del_server(server->global_list, cache->context); + while (silc_idcache_list_next(list, &cache)) + silc_idlist_del_server(server->global_list, cache->context); + } + if (list) + silc_idcache_list_free(list); + silc_idcache_free(server->local_list->clients); silc_idcache_free(server->local_list->servers); silc_idcache_free(server->local_list->channels); @@ -137,7 +199,7 @@ static bool silc_server_listen(SilcServer server, const char *server_ip, { *sock = silc_net_create_server(port, server_ip); if (*sock < 0) { - SILC_LOG_ERROR(("Could not create server listener: %s on %hu", + SILC_SERVER_LOG_ERROR(("Could not create server listener: %s on %hu", server_ip, port)); return FALSE; } @@ -152,7 +214,7 @@ bool silc_server_init_secondary(SilcServer server) SilcSocketConnection newsocket = NULL; SilcServerConfigServerInfoInterface *interface; - for (interface = server->config->server_info->secondary; interface; + for (interface = server->config->server_info->secondary; interface; interface = interface->next, sock++) { if (!silc_server_listen(server, @@ -167,6 +229,7 @@ bool silc_server_init_secondary(SilcServer server) silc_socket_alloc(sock_list[sock], SILC_SOCKET_TYPE_SERVER, NULL, &newsocket); server->sockets[sock_list[sock]] = newsocket; + SILC_SET_LISTENER(newsocket); /* Perform name and address lookups to resolve the listenning address and port. */ @@ -187,10 +250,10 @@ bool silc_server_init_secondary(SilcServer server) 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); + silc_server_accept_new_connection, + (void *)server, 0, 0, + SILC_TASK_FD, + SILC_TASK_PRI_NORMAL); } return TRUE; @@ -220,9 +283,17 @@ bool silc_server_init(SilcServer server) server->starttime = time(NULL); /* Take config object for us */ - silc_server_config_ref(&server->config_ref, server->config, + 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_debug = TRUE; + silc_log_set_debug_string(server->config->debug_string); + } +#endif /* SILC_DEBUG */ + /* 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; @@ -255,7 +326,8 @@ bool silc_server_init(SilcServer server) silc_pkcs_private_key_set(server->pkcs, server->private_key); /* Initialize the scheduler */ - server->schedule = silc_schedule_init(server->config->param.connections_max); + server->schedule = silc_schedule_init(server->config->param.connections_max, + server); if (!server->schedule) goto err; @@ -277,7 +349,7 @@ bool silc_server_init(SilcServer server) server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL); /* Init watcher list */ - server->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); @@ -310,6 +382,7 @@ bool silc_server_init(SilcServer server) is sent as argument for fast referencing in the future. */ silc_socket_alloc(sock, SILC_SOCKET_TYPE_SERVER, NULL, &newsocket); server->sockets[sock] = newsocket; + SILC_SET_LISTENER(newsocket); /* Perform name and address lookups to resolve the listenning address and port. */ @@ -336,7 +409,6 @@ bool silc_server_init(SilcServer server) server->id = id; server->id_string = silc_id_id2str(id, SILC_ID_SERVER); server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER); - server->id_type = SILC_ID_SERVER; server->server_name = server->config->server_info->server_name; server->config->server_info->server_name = NULL; @@ -362,15 +434,8 @@ bool silc_server_init(SilcServer server) /* Register protocols */ silc_server_protocols_register(); - /* Add the first task to the scheduler. This is task that is executed by - 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, 0, - silc_server_connect_to_router, - (void *)server, 0, 1, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); + /* Create connections to configured routers. */ + silc_server_create_connections(server); /* Add listener task to the scheduler. This task receives new connections to the server. This task remains on the queue until the end of the @@ -383,7 +448,7 @@ bool silc_server_init(SilcServer server) if (silc_server_init_secondary(server) == FALSE) goto err; - + server->listenning = TRUE; /* If server connections has been configured then we must be router as @@ -409,27 +474,23 @@ bool silc_server_init(SilcServer server) /* Clients local list */ server->purge_i = purge = silc_calloc(1, sizeof(*purge)); purge->cache = server->local_list->clients; - purge->schedule = server->schedule; purge->timeout = 600; - silc_schedule_task_add(purge->schedule, 0, - silc_idlist_purge, + silc_schedule_task_add(server->schedule, 0, silc_idlist_purge, (void *)purge, purge->timeout, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); /* Clients global list */ server->purge_g = purge = silc_calloc(1, sizeof(*purge)); purge->cache = server->global_list->clients; - purge->schedule = server->schedule; purge->timeout = 300; - silc_schedule_task_add(purge->schedule, 0, - silc_idlist_purge, + silc_schedule_task_add(server->schedule, 0, silc_idlist_purge, (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, + if (server->server_type != SILC_ROUTER) + silc_schedule_task_add(server->schedule, 0, silc_server_get_stats, server, 10, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); @@ -447,6 +508,31 @@ bool silc_server_init(SilcServer server) return FALSE; } +/* Task callback to close a socket connection after rehash */ + +SILC_TASK_CALLBACK(silc_server_rehash_close_connection) +{ + SilcServer server = context; + SilcSocketConnection sock = server->sockets[fd]; + + if (!sock) + return; + + SILC_LOG_INFO(("Connection %s:%d [%s] is unconfigured", + sock->hostname, sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); + 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"); + if (sock->user_data) + 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. @@ -471,7 +557,7 @@ bool silc_server_rehash(SilcServer server) /* Reinit scheduler if necessary */ if (newconfig->param.connections_max > server->config->param.connections_max) - if (!silc_schedule_reinit(server->schedule, + if (!silc_schedule_reinit(server->schedule, newconfig->param.connections_max)) return FALSE; @@ -484,7 +570,7 @@ bool silc_server_rehash(SilcServer server) /* Update the idcache list with a fresh pointer */ silc_free(server->id_entry->server_name); server->id_entry->server_name = strdup(server->server_name); - if (!silc_idcache_del_by_context(server->local_list->servers, + if (!silc_idcache_del_by_context(server->local_list->servers, server->id_entry)) return FALSE; if (!silc_idcache_add(server->local_list->servers, @@ -515,12 +601,103 @@ bool silc_server_rehash(SilcServer server) silc_pkcs_private_key_set(server->pkcs, server->private_key); } - /* 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 for unconfigured server and router connections and close + connections that were unconfigured. */ + + if (server->config->routers) { + SilcServerConfigRouter *ptr; + SilcServerConfigRouter *newptr; + bool 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 */ + SilcSocketConnection sock; + sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER, + ptr->host, ptr->port); + if (sock && !SILC_IS_LISTENER(sock)) + silc_schedule_task_add(server->schedule, sock->sock, + silc_server_rehash_close_connection, + server, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + } + } + } + + if (server->config->servers) { + SilcServerConfigServer *ptr; + SilcServerConfigServer *newptr; + bool 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 */ + SilcSocketConnection sock; + sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_SERVER, + ptr->host, 0); + if (sock && !SILC_IS_LISTENER(sock)) + silc_schedule_task_add(server->schedule, sock->sock, + silc_server_rehash_close_connection, + server, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + } + } + } + + if (server->config->clients) { + SilcServerConfigClient *ptr; + SilcServerConfigClient *newptr; + bool 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 */ + SilcSocketConnection sock; + sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_CLIENT, + ptr->host, 0); + if (sock) + silc_schedule_task_add(server->schedule, sock->sock, + silc_server_rehash_close_connection, + server, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + } + } + } + + /* Create connections after rehash */ + silc_server_create_connections(server); /* Check whether our router status has changed */ if (newconfig->servers) { @@ -548,6 +725,16 @@ bool silc_server_rehash(SilcServer server) 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_debug = TRUE; + silc_log_set_debug_string(server->config->debug_string); + } else { + silc_debug = FALSE; + } +#endif /* SILC_DEBUG */ + SILC_LOG_DEBUG(("Server rehashed")); return TRUE; @@ -572,22 +759,52 @@ void silc_server_run(SilcServer server) void silc_server_stop(SilcServer server) { - SILC_LOG_DEBUG(("Stopping 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; - silc_socket_free(server->sockets[i]); + 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; + } } - silc_free(server->sockets); - server->sockets = 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(); @@ -680,12 +897,21 @@ void silc_server_start_key_exchange(SilcServer server, SILC_TASK_CALLBACK(silc_server_connect_to_router_retry) { + SilcServer server = app_context; SilcServerConnection sconn = (SilcServerConnection)context; - SilcServer server = sconn->server; 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 */ @@ -711,6 +937,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry) 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); @@ -724,11 +953,19 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry) SILC_TASK_CALLBACK(silc_server_connect_router) { + SilcServer server = app_context; SilcServerConnection sconn = (SilcServerConnection)context; - SilcServer server = sconn->server; 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)); @@ -760,8 +997,12 @@ SILC_TASK_CALLBACK(silc_server_connect_router) silc_server_connect_to_router_retry, context, 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - else + else { silc_server_config_unref(&sconn->conn); + silc_free(sconn->remote_host); + silc_free(sconn->backup_replace_ip); + silc_free(sconn); + } return; } @@ -774,26 +1015,25 @@ SILC_TASK_CALLBACK(silc_server_connect_router) server to do authentication and key exchange with our router - called from schedule. */ -SILC_TASK_CALLBACK(silc_server_connect_to_router) +SILC_TASK_CALLBACK_GLOBAL(silc_server_connect_to_router) { SilcServer server = (SilcServer)context; SilcServerConnection sconn; SilcServerConfigRouter *ptr; - SILC_LOG_DEBUG(("Connecting to router(s)")); + /* Don't connect if we are shutting down. */ + if (server->server_shutdown) + return; - if (server->server_type == SILC_SERVER) { - SILC_LOG_DEBUG(("We are normal server")); - } else if (server->server_type == SILC_ROUTER) { - SILC_LOG_DEBUG(("We are router")); - } else { - SILC_LOG_DEBUG(("We are backup router/normal server")); - } + 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), server will be standalone")); + SILC_LOG_DEBUG(("No router(s), we are standalone")); server->standalone = TRUE; return; } @@ -812,21 +1052,59 @@ SILC_TASK_CALLBACK(silc_server_connect_to_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 connected to this host already */ - if (silc_server_num_sockets_by_remote(server, + /* 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->server = server; sconn->remote_host = strdup(ptr->host); sconn->remote_port = ptr->port; sconn->backup = ptr->backup_router; @@ -874,14 +1152,26 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) 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); - silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); - silc_server_disconnect_remote(server, sock, - SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL); return; } @@ -905,14 +1195,26 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) 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); - silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); - silc_server_disconnect_remote(server, sock, - SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL); return; } silc_ske_free_key_material(ctx->keymat); @@ -967,9 +1269,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) silc_free(sconn->remote_host); silc_free(sconn->backup_replace_ip); silc_free(sconn); - silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); - silc_server_disconnect_remote(server, sock, + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL); return; } @@ -1014,9 +1314,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) SilcServer server = (SilcServer)ctx->server; SilcServerConnection sconn = (SilcServerConnection)ctx->context; SilcSocketConnection sock = ctx->sock; - SilcServerEntry id_entry; + SilcServerEntry id_entry = NULL; SilcBuffer packet; - SilcServerHBContext hb_context; unsigned char *id_string; SilcUInt32 id_len; SilcIDListData idata; @@ -1029,8 +1328,20 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) protocol->state == SILC_PROTOCOL_STATE_FAILURE) { /* Error occured during protocol */ silc_free(ctx->dest_id); + sock->protocol = NULL; silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED, NULL); + sock->protocol = protocol; + + /* 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); + goto out2; + } + goto out; } @@ -1082,15 +1393,18 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) SILC_LOG_DEBUG(("New server id(%s)", silc_id_render(ctx->dest_id, SILC_ID_SERVER))); - /* Add the connected router to global server list */ + /* 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(sock->hostname), SILC_ROUTER, ctx->dest_id, NULL, sock); if (!id_entry) { silc_free(ctx->dest_id); SILC_LOG_ERROR(("Cannot add new server entry to cache")); + sock->protocol = NULL; silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED, NULL); + sock->protocol = protocol; goto out; } @@ -1099,24 +1413,21 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) sock->user_data = (void *)id_entry; sock->type = SILC_SOCKET_TYPE_ROUTER; idata = (SilcIDListData)sock->user_data; - idata->status |= SILC_IDLIST_STATUS_REGISTERED; + idata->status |= (SILC_IDLIST_STATUS_REGISTERED | + SILC_IDLIST_STATUS_LOCAL); conn = sconn->conn.ref_ptr; param = &server->config->param; if (conn && conn->param) param = conn->param; - /* Perform keepalive. The `hb_context' will be freed automatically - when finally calling the silc_socket_free function. */ - hb_context = silc_calloc(1, sizeof(*hb_context)); - hb_context->server = server; - silc_socket_set_heartbeat(sock, param->keepalive_secs, hb_context, + /* Perform keepalive. */ + silc_socket_set_heartbeat(sock, param->keepalive_secs, server, silc_server_perform_heartbeat, server->schedule); /* Register re-key timeout */ idata->rekey->timeout = param->key_exchange_rekey; - idata->rekey->context = (void *)server; silc_schedule_task_add(server->schedule, sock->sock, silc_server_rekey_callback, (void *)sock, idata->rekey->timeout, 0, @@ -1125,40 +1436,52 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) 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; - /* If we are router then announce our possible servers. */ - if (server->server_type == SILC_ROUTER) - silc_server_announce_servers(server, FALSE, 0, - server->router->connection); + /* 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, server->router->connection); - silc_server_announce_channels(server, 0, server->router->connection); + /* 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)); + } -#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 */ + silc_server_backup_add(server, server->id_entry, sock->ip, + sconn->remote_port, TRUE); } } 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); } sock->protocol = NULL; + out: /* Call the completion callback to indicate that we've connected to the router */ - if (sconn->callback) + if (sconn && sconn->callback) (*sconn->callback)(server, id_entry, sconn->callback_context); - out: /* Free the temporary connection data context */ if (sconn) { silc_server_config_unref(&sconn->conn); @@ -1169,6 +1492,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) if (sconn == server->router_conn) server->router_conn = NULL; + out2: /* Free the protocol object */ if (sock->protocol == protocol) sock->protocol = NULL; @@ -1200,10 +1524,6 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, SilcServerConfigDeny *deny; int port; - context = (void *)server; - - SILC_LOG_DEBUG(("Start")); - /* Check whether we could resolve both IP and FQDN. */ if (!sock->ip || (!strcmp(sock->ip, sock->hostname) && server->config->require_reverse_lookup)) { @@ -1224,12 +1544,21 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, 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->sock); SILC_LOG_INFO(("Incoming connection %s (%s)", sock->hostname, sock->ip)); /* Listenning port */ + if (!server->sockets[(SilcUInt32)proto_ctx->context]) { + silc_server_disconnect_remote(server, sock, + SILC_STATUS_ERR_RESOURCE_LIMIT, + "Connection refused"); + server->stat.conn_failures++; + silc_free(proto_ctx); + return; + } port = server->sockets[(SilcUInt32)proto_ctx->context]->port; /* Check whether this connection is denied to connect to us. */ @@ -1296,6 +1625,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, initiator of the protocol thus we will wait for initiation from there before we start the protocol. */ server->stat.auth_attempts++; + SILC_LOG_DEBUG(("Starting key exchange protocol")); silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, &sock->protocol, proto_ctx, silc_server_accept_new_connection_second); @@ -1381,6 +1711,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) if ((protocol->state == SILC_PROTOCOL_STATE_ERROR) || (protocol->state == SILC_PROTOCOL_STATE_FAILURE)) { /* Error occured during protocol */ + SILC_LOG_DEBUG(("Error in key exchange protocol")); silc_protocol_free(protocol); sock->protocol = NULL; silc_ske_free_key_material(ctx->keymat); @@ -1393,11 +1724,19 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) silc_server_config_unref(&ctx->sconfig); silc_server_config_unref(&ctx->rconfig); silc_free(ctx); - silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); - silc_server_disconnect_remote(server, sock, - SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, - NULL); + + if (!SILC_IS_DISCONNECTING(sock)) { + SILC_LOG_INFO(("Key exchange failed for %s:%d [%s]", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); + silc_server_disconnect_remote(server, sock, + SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, + NULL); + } + server->stat.auth_failures++; return; } @@ -1413,6 +1752,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) ctx->ske->prop->hmac, ctx->ske->prop->group, ctx->responder)) { + SILC_LOG_ERROR(("Error setting key material in use")); silc_protocol_free(protocol); sock->protocol = NULL; silc_ske_free_key_material(ctx->keymat); @@ -1425,9 +1765,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) silc_server_config_unref(&ctx->sconfig); silc_server_config_unref(&ctx->rconfig); silc_free(ctx); - silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); - silc_server_disconnect_remote(server, sock, + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL); server->stat.auth_failures++; return; @@ -1458,6 +1796,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) 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_LOG_DEBUG(("Starting connection authentication protocol")); silc_protocol_alloc(SILC_PROTOCOL_SERVER_CONNECTION_AUTH, &sock->protocol, proto_ctx, silc_server_accept_new_connection_final); @@ -1473,6 +1812,16 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) SILC_TASK_PRI_LOW); } +/* After this is called, server don't wait for backup router anymore. + This gets called automatically even after we have backup router + connection established. */ + +SILC_TASK_CALLBACK(silc_server_backup_router_wait) +{ + SilcServer server = context; + server->wait_backup = FALSE; +} + /* Final part of accepting new connection. The connection has now been authenticated and keys has been exchanged. We also know whether this is client or server connection. */ @@ -1484,16 +1833,14 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) (SilcServerConnAuthInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; SilcSocketConnection sock = ctx->sock; - SilcServerHBContext hb_context; SilcUnknownEntry entry = (SilcUnknownEntry)sock->user_data; void *id_entry; - SilcUInt32 hearbeat_timeout = server->config->param.keepalive_secs; - - SILC_LOG_DEBUG(("Start")); + SilcServerConfigConnParams *param = &server->config->param; if (protocol->state == SILC_PROTOCOL_STATE_ERROR || protocol->state == SILC_PROTOCOL_STATE_FAILURE) { /* Error occured during protocol */ + SILC_LOG_DEBUG(("Error during authentication protocol")); silc_protocol_free(protocol); sock->protocol = NULL; if (ctx->packet) @@ -1505,10 +1852,17 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) silc_server_config_unref(&ctx->sconfig); silc_server_config_unref(&ctx->rconfig); silc_free(ctx); - silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); - silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED, - NULL); + + if (!SILC_IS_DISCONNECTING(sock)) { + SILC_LOG_INFO(("Authentication failed for %s:%d [%s]", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED, + NULL); + } server->stat.auth_failures++; return; } @@ -1529,6 +1883,37 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) goto out; } + /* If we are primary router and we have backup router configured + but it has not connected to use yet, do not accept any other + connection. */ + if (server->wait_backup && server->server_type == SILC_ROUTER && + !server->backup_router) { + SilcServerConfigRouter *router; + router = silc_server_config_get_backup_router(server); + if (router && strcmp(server->config->server_info->primary->server_ip, + sock->ip) && + silc_server_find_socket_by_host(server, + SILC_SOCKET_TYPE_SERVER, + router->backup_replace_ip, 0)) { + SILC_LOG_INFO(("Will not accept connections because we do " + "not have backup router connection established")); + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, + SILC_STATUS_ERR_PERM_DENIED, + "We do not have connection to backup " + "router established, try later"); + silc_free(sock->user_data); + server->stat.auth_failures++; + + /* From here on, wait 10 seconds for the backup router to appear. */ + silc_schedule_task_add(server->schedule, 0, + silc_server_backup_router_wait, + (void *)server, 10, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + goto out; + } + } + SILC_LOG_DEBUG(("Remote host is client")); SILC_LOG_INFO(("Connection %s (%s) is client", sock->hostname, sock->ip)); @@ -1541,11 +1926,13 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) if (!client) { SILC_LOG_ERROR(("Could not add new client to cache")); silc_free(sock->user_data); - silc_server_disconnect_remote(server, sock, + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, SILC_STATUS_ERR_AUTH_FAILED, NULL); server->stat.auth_failures++; goto out; } + entry->data.status |= SILC_IDLIST_STATUS_LOCAL; /* Statistics */ server->stat.my_clients++; @@ -1554,8 +1941,22 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) /* Get connection parameters */ if (conn->param) { - if (conn->param->keepalive_secs) - hearbeat_timeout = conn->param->keepalive_secs; + param = conn->param; + + if (!param->keepalive_secs) + param->keepalive_secs = server->config->param.keepalive_secs; + + if (!param->qos && server->config->param.qos) { + param->qos = server->config->param.qos; + param->qos_rate_limit = server->config->param.qos_rate_limit; + param->qos_bytes_limit = server->config->param.qos_bytes_limit; + param->qos_limit_sec = server->config->param.qos_limit_sec; + param->qos_limit_usec = server->config->param.qos_limit_usec; + } + + /* Check if to be anonymous connection */ + if (param->anonymous) + client->mode |= SILC_UMODE_ANONYMOUS; } id_entry = (void *)client; @@ -1573,20 +1974,49 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) SilcServerConfigServer *sconn = ctx->sconfig.ref_ptr; SilcServerConfigRouter *rconn = ctx->rconfig.ref_ptr; + /* If we are backup router and this is incoming server connection + and we do not have connection to primary router, do not allow + the connection. */ + if (server->server_type == SILC_BACKUP_ROUTER && + ctx->conn_type == SILC_SOCKET_TYPE_SERVER && + !SILC_PRIMARY_ROUTE(server)) { + SILC_LOG_INFO(("Will not accept server connection because we do " + "not have primary router connection established")); + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, + SILC_STATUS_ERR_PERM_DENIED, + "We do not have connection to primary " + "router established, try later"); + silc_free(sock->user_data); + server->stat.auth_failures++; + goto out; + } + if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) { /* Verify whether this connection is after all allowed to connect */ if (!silc_server_connection_allowed(server, sock, ctx->conn_type, &server->config->param, rconn ? rconn->param : NULL, ctx->ske)) { + silc_free(sock->user_data); server->stat.auth_failures++; goto out; } if (rconn) { if (rconn->param) { - if (rconn->param->keepalive_secs) - hearbeat_timeout = rconn->param->keepalive_secs; + param = rconn->param; + + if (!param->keepalive_secs) + param->keepalive_secs = server->config->param.keepalive_secs; + + if (!param->qos && server->config->param.qos) { + param->qos = server->config->param.qos; + param->qos_rate_limit = server->config->param.qos_rate_limit; + param->qos_bytes_limit = server->config->param.qos_bytes_limit; + param->qos_limit_sec = server->config->param.qos_limit_sec; + param->qos_limit_usec = server->config->param.qos_limit_usec; + } } initiator = rconn->initiator; @@ -1603,19 +2033,61 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) &server->config->param, sconn ? sconn->param : NULL, ctx->ske)) { + silc_free(sock->user_data); server->stat.auth_failures++; goto out; } if (sconn) { if (sconn->param) { - if (sconn->param->keepalive_secs) - hearbeat_timeout = sconn->param->keepalive_secs; + param = sconn->param; + + if (!param->keepalive_secs) + param->keepalive_secs = server->config->param.keepalive_secs; + + if (!param->qos && server->config->param.qos) { + param->qos = server->config->param.qos; + param->qos_rate_limit = server->config->param.qos_rate_limit; + param->qos_bytes_limit = server->config->param.qos_bytes_limit; + param->qos_limit_sec = server->config->param.qos_limit_sec; + param->qos_limit_usec = server->config->param.qos_limit_usec; + } } backup_router = sconn->backup_router; } } + /* If we are primary router and we have backup router configured + but it has not connected to use yet, do not accept any other + connection. */ + if (server->wait_backup && server->server_type == SILC_ROUTER && + !server->backup_router && !backup_router) { + SilcServerConfigRouter *router; + router = silc_server_config_get_backup_router(server); + if (router && strcmp(server->config->server_info->primary->server_ip, + sock->ip) && + silc_server_find_socket_by_host(server, + SILC_SOCKET_TYPE_SERVER, + router->backup_replace_ip, 0)) { + SILC_LOG_INFO(("Will not accept connections because we do " + "not have backup router connection established")); + sock->protocol = NULL; + silc_server_disconnect_remote(server, sock, + SILC_STATUS_ERR_PERM_DENIED, + "We do not have connection to backup " + "router established, try later"); + silc_free(sock->user_data); + server->stat.auth_failures++; + + /* From here on, wait 10 seconds for the backup router to appear. */ + silc_schedule_task_add(server->schedule, 0, + silc_server_backup_router_wait, + (void *)server, 10, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + goto out; + } + } + SILC_LOG_DEBUG(("Remote host is %s", ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? "server" : (backup_router ? @@ -1645,35 +2117,43 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) if (!new_server) { SILC_LOG_ERROR(("Could not add new server to cache")); silc_free(sock->user_data); - silc_server_disconnect_remote(server, sock, + sock->protocol = NULL; + 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) { - server->stat.my_servers++; - } else { - server->stat.my_routers++; - server->stat.routers++; - } - server->stat.servers++; + entry->data.status |= SILC_IDLIST_STATUS_LOCAL; id_entry = (void *)new_server; /* If the incoming connection is router and marked as backup router then add it to be one of our backups */ if (ctx->conn_type == SILC_SOCKET_TYPE_ROUTER && backup_router) { - silc_server_backup_add(server, new_server, backup_replace_ip, - backup_replace_port, backup_local); - /* Change it back to SERVER type since that's what it really is. */ if (backup_local) ctx->conn_type = SILC_SOCKET_TYPE_SERVER; - new_server->server_type = SILC_BACKUP_ROUTER; + + SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE, + ("Backup router %s is now online", + sock->hostname)); + + /* Remove the backup waiting with timeout */ + silc_schedule_task_add(server->schedule, 0, + silc_server_backup_router_wait, + (void *)server, 5, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + } + + /* Statistics */ + if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER) { + server->stat.my_servers++; + } else { + server->stat.my_routers++; + server->stat.routers++; } + server->stat.servers++; /* Check whether this connection is to be our primary router connection if we do not already have the primary route. */ @@ -1709,18 +2189,21 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) /* Connection has been fully established now. Everything is ok. */ SILC_LOG_DEBUG(("New connection authenticated")); - /* Perform keepalive. The `hb_context' will be freed automatically - when finally calling the silc_socket_free function. */ - hb_context = silc_calloc(1, sizeof(*hb_context)); - hb_context->server = server; - silc_socket_set_heartbeat(sock, hearbeat_timeout, hb_context, - silc_server_perform_heartbeat, - server->schedule); + /* Perform keepalive. */ + if (param->keepalive_secs) + silc_socket_set_heartbeat(sock, param->keepalive_secs, server, + silc_server_perform_heartbeat, + server->schedule); + + /* Perform Quality of Service */ + if (param->qos) + silc_socket_set_qos(sock, param->qos_rate_limit, param->qos_bytes_limit, + param->qos_limit_sec, param->qos_limit_usec, + server->schedule); out: - silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); - silc_protocol_free(protocol); + if (sock->protocol == protocol) + silc_protocol_free(protocol); if (ctx->packet) silc_packet_context_free(ctx->packet); if (ctx->ske) @@ -1744,19 +2227,22 @@ SILC_TASK_CALLBACK(silc_server_packet_process) SilcCipher cipher = NULL; SilcHmac hmac = NULL; SilcUInt32 sequence = 0; + bool local_is_router; int ret; - if (!sock) + if (!sock) { + SILC_LOG_DEBUG(("Unknown socket connection")); return; - - SILC_LOG_DEBUG(("Processing packet")); + } /* Packet sending */ if (type == SILC_TASK_WRITE) { /* Do not send data to disconnected connection */ - if (SILC_IS_DISCONNECTED(sock)) + if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) { + SILC_LOG_DEBUG(("Disconnected socket connection, cannot send")); return; + } server->stat.packets_sent++; @@ -1768,6 +2254,14 @@ SILC_TASK_CALLBACK(silc_server_packet_process) if (ret == -2) return; + /* The packet has been sent and now it is time to set the connection + back to only for input. When there is again some outgoing data + available for this connection it will be set for output as well. + This call clears the output setting and sets it only for input. */ + SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd); + SILC_UNSET_OUTBUF_PENDING(sock); + silc_buffer_clear(sock->outbuf); + if (ret == -1) { SILC_LOG_ERROR(("Error sending packet to connection " "%s:%d [%s]", sock->hostname, sock->port, @@ -1775,17 +2269,12 @@ SILC_TASK_CALLBACK(silc_server_packet_process) sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : "Router"))); - return; - } - - /* The packet has been sent and now it is time to set the connection - back to only for input. When there is again some outgoing data - available for this connection it will be set for output as well. - This call clears the output setting and sets it only for input. */ - SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd); - SILC_UNSET_OUTBUF_PENDING(sock); - silc_buffer_clear(sock->outbuf); + if (sock->user_data) + silc_server_free_sock_user_data(server, sock, NULL); + SILC_SET_DISCONNECTING(sock); + silc_server_close_connection(server, sock); + } return; } @@ -1795,13 +2284,19 @@ SILC_TASK_CALLBACK(silc_server_packet_process) ret = silc_packet_receive(sock); if (ret < 0) { - if (ret == -1) + if (ret == -1) { SILC_LOG_ERROR(("Error receiving packet from connection " "%s:%d [%s] %s", sock->hostname, sock->port, (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : "Router"), strerror(errno))); + + if (sock->user_data) + silc_server_free_sock_user_data(server, sock, NULL); + SILC_SET_DISCONNECTING(sock); + silc_server_close_connection(server, sock); + } return; } @@ -1819,22 +2314,28 @@ SILC_TASK_CALLBACK(silc_server_packet_process) } SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock)); - SILC_SET_DISCONNECTING(sock); if (sock->user_data) { char tmp[128]; + + /* If backup disconnected then mark that resuming willl not be allowed */ + if (server->server_type == SILC_ROUTER && !server->backup_router && + sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) { + SilcServerEntry server_entry = sock->user_data; + if (server_entry->server_type == SILC_BACKUP_ROUTER) + server->backup_closed = TRUE; + } + if (silc_socket_get_error(sock, tmp, sizeof(tmp) - 1)) silc_server_free_sock_user_data(server, sock, tmp); else silc_server_free_sock_user_data(server, sock, NULL); } else if (server->router_conn && server->router_conn->sock == sock && - !server->router && server->standalone) - silc_schedule_task_add(server->schedule, 0, - silc_server_connect_to_router, - server, 1, 0, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); + !server->router && server->standalone) { + silc_server_create_connections(server); + } + SILC_SET_DISCONNECTING(sock); silc_server_close_connection(server, sock); return; } @@ -1846,8 +2347,6 @@ SILC_TASK_CALLBACK(silc_server_packet_process) return; } - server->stat.packets_received++; - /* Get keys and stuff from ID entry */ idata = (SilcIDListData)sock->user_data; if (idata) { @@ -1856,23 +2355,33 @@ SILC_TASK_CALLBACK(silc_server_packet_process) sequence = idata->psn_receive; } - /* Process the packet. This will call the parser that will then + /* Then, process the packet. This will call the parser that will then decrypt and parse the packet. */ - ret = silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? - TRUE : FALSE, cipher, hmac, sequence, + + local_is_router = (server->server_type == SILC_ROUTER); + + /* If socket connection is our primary, we are backup and we are doing + backup resuming, we won't process the packet as being a router + (affects channel message decryption). */ + if (server->backup_router && SILC_SERVER_IS_BACKUP(sock) && + SILC_PRIMARY_ROUTE(server) == sock) + local_is_router = FALSE; + + ret = silc_packet_receive_process(sock, local_is_router, + cipher, hmac, sequence, silc_server_packet_parse, server); - /* If this socket connection is not authenticated yet and the packet - processing failed we will drop the connection since it can be - a malicious flooder. */ - if (sock->type == SILC_SOCKET_TYPE_UNKNOWN && ret == FALSE && - (!sock->protocol || sock->protocol->protocol->type == - SILC_PROTOCOL_SERVER_KEY_EXCHANGE)) { - SILC_LOG_DEBUG(("Bad data sent from unknown connection %d", sock->sock)); - SILC_SET_DISCONNECTING(sock); + /* If processing failed the connection is closed. */ + if (!ret) { + /* On packet processing errors we may close our primary router + connection but won't become primary router if we are the backup + since this is local error condition. */ + if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router) + server->backup_noswitch = TRUE; if (sock->user_data) silc_server_free_sock_user_data(server, sock, NULL); + SILC_SET_DISCONNECTING(sock); silc_server_close_connection(server, sock); } } @@ -1888,7 +2397,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) SilcIDListData idata = (SilcIDListData)sock->user_data; int ret; - SILC_LOG_DEBUG(("Start")); + server->stat.packets_received++; /* Parse the packet */ if (parse_ctx->normal) @@ -1897,23 +2406,27 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) ret = silc_packet_parse_special(packet, idata ? idata->receive_key : NULL); /* If entry is disabled ignore what we got. */ - if (ret != SILC_PACKET_RESUME_ROUTER && - idata && idata->status & SILC_IDLIST_STATUS_DISABLED) { + if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED && + ret != SILC_PACKET_HEARTBEAT && ret != SILC_PACKET_RESUME_ROUTER && + ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE) { SILC_LOG_DEBUG(("Connection is disabled")); goto out; } - if (ret == SILC_PACKET_NONE) + if (ret == SILC_PACKET_NONE) { + SILC_LOG_DEBUG(("Error parsing packet")); goto out; + } /* Check that the the current client ID is same as in the client's packet. */ if (sock->type == SILC_SOCKET_TYPE_CLIENT) { SilcClientEntry client = (SilcClientEntry)sock->user_data; - if (client && client->id) { + if (client && client->id && packet->src_id) { void *id = silc_id_str2id(packet->src_id, packet->src_id_len, packet->src_id_type); if (!id || !SILC_ID_CLIENT_COMPARE(client->id, id)) { silc_free(id); + SILC_LOG_DEBUG(("Packet source is not same as sender")); goto out; } silc_free(id); @@ -1923,7 +2436,7 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) if (server->server_type == SILC_ROUTER) { /* Route the packet if it is not destined to us. Other ID types but server are handled separately after processing them. */ - if (!(packet->flags & SILC_PACKET_FLAG_BROADCAST) && + if (packet->dst_id && !(packet->flags & SILC_PACKET_FLAG_BROADCAST) && packet->dst_id_type == SILC_ID_SERVER && sock->type != SILC_SOCKET_TYPE_CLIENT && memcmp(packet->dst_id, server->id_string, server->id_string_len)) { @@ -1945,19 +2458,17 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) /* Parse the incoming packet type */ silc_server_packet_parse_type(server, sock, packet); - if (server->server_type == SILC_ROUTER) { - /* Broadcast packet if it is marked as broadcast packet and it is - originated from router and we are router. */ - if (sock->type == SILC_SOCKET_TYPE_ROUTER && - packet->flags & SILC_PACKET_FLAG_BROADCAST && - !server->standalone) { - /* Broadcast to our primary route */ - silc_server_packet_broadcast(server, server->router->connection, packet); - - /* If we have backup routers then we need to feed all broadcast - data to those servers. */ - silc_server_backup_broadcast(server, sock, packet); - } + /* Broadcast packet if it is marked as broadcast packet and it is + originated from router and we are router. */ + if (server->server_type == SILC_ROUTER && + sock->type == SILC_SOCKET_TYPE_ROUTER && + packet->flags & SILC_PACKET_FLAG_BROADCAST) { + /* Broadcast to our primary route */ + silc_server_packet_broadcast(server, SILC_PRIMARY_ROUTE(server), packet); + + /* If we have backup routers then we need to feed all broadcast + data to those servers. */ + silc_server_backup_broadcast(server, sock, packet); } out: @@ -1974,6 +2485,7 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context, SilcServer server = (SilcServer)context; SilcSocketConnection sock = parser_context->sock; SilcIDListData idata = (SilcIDListData)sock->user_data; + bool ret; if (idata) idata->psn_receive = parser_context->packet->sequence + 1; @@ -1982,26 +2494,40 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context, process all packets synchronously, since there might be packets in queue that we are not able to decrypt without first processing the packets before them. */ - if ((parser_context->packet->type == SILC_PACKET_REKEY || - parser_context->packet->type == SILC_PACKET_REKEY_DONE) || - (sock->protocol && sock->protocol->protocol && - (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE || - sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY))) { - silc_server_packet_parse_real(server->schedule, 0, sock->sock, + if (sock->protocol && sock->protocol->protocol && + (sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_KEY_EXCHANGE || + sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY)) { + silc_server_packet_parse_real(server->schedule, server, 0, sock->sock, parser_context); /* Reprocess data since we'll return FALSE here. This is because the idata->receive_key might have become valid in the last packet and we want to call this processor with valid cipher. */ if (idata) - silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? - TRUE : FALSE, idata->receive_key, + ret = silc_packet_receive_process( + sock, server->server_type == SILC_ROUTER, + idata->receive_key, idata->hmac_receive, idata->psn_receive, silc_server_packet_parse, server); else - silc_packet_receive_process(sock, server->server_type == SILC_ROUTER ? - TRUE : FALSE, NULL, NULL, 0, + ret = silc_packet_receive_process( + sock, server->server_type == SILC_ROUTER, + NULL, NULL, 0, silc_server_packet_parse, server); + + if (!ret) { + /* On packet processing errors we may close our primary router + connection but won't become primary router if we are the backup + since this is local error condition. */ + if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router) + server->backup_noswitch = TRUE; + + if (sock->user_data) + silc_server_free_sock_user_data(server, sock, NULL); + SILC_SET_DISCONNECTING(sock); + silc_server_close_connection(server, sock); + } + return FALSE; } @@ -2018,7 +2544,7 @@ bool silc_server_packet_parse(SilcPacketParserContext *parser_context, case SILC_SOCKET_TYPE_SERVER: case SILC_SOCKET_TYPE_ROUTER: /* Packets from servers are parsed immediately */ - silc_server_packet_parse_real(server->schedule, 0, sock->sock, + silc_server_packet_parse_real(server->schedule, server, 0, sock->sock, parser_context); break; default: @@ -2038,7 +2564,8 @@ void silc_server_packet_parse_type(SilcServer server, SilcPacketType type = packet->type; SilcIDListData idata = (SilcIDListData)sock->user_data; - SILC_LOG_DEBUG(("Parsing packet type %d", type)); + SILC_LOG_DEBUG(("Received %s packet [flags %d]", + silc_get_packet_name(type), packet->flags)); /* Parse the packet type */ switch (type) { @@ -2047,8 +2574,6 @@ void silc_server_packet_parse_type(SilcServer server, SilcStatus status; char *message = NULL; - SILC_LOG_DEBUG(("Disconnect packet")); - if (packet->flags & SILC_PACKET_FLAG_LIST) break; if (packet->buffer->len < 1) @@ -2057,13 +2582,32 @@ void silc_server_packet_parse_type(SilcServer server, 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); + message = silc_memdup(packet->buffer->data + 1, + packet->buffer->len - 1); - SILC_LOG_ERROR(("Disconnected by %s (%s): %s (%d) %s", - sock->ip, sock->hostname, - silc_get_status_message(status), status, - message ? message : "")); + SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", + sock->ip, sock->hostname, + silc_get_status_message(status), status, + message ? message : "")); silc_free(message); + + /* Do not switch to backup in case of error */ + server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE); + + /* If backup disconnected then mark that resuming willl not be allowed */ + if (server->server_type == SILC_ROUTER && !server->backup_router && + sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) { + SilcServerEntry server_entry = sock->user_data; + if (server_entry->server_type == SILC_BACKUP_ROUTER) + server->backup_closed = TRUE; + } + + /* Handle the disconnection from our end too */ + if (sock->user_data && SILC_IS_LOCAL(sock->user_data)) + silc_server_free_sock_user_data(server, sock, NULL); + SILC_SET_DISCONNECTING(sock); + silc_server_close_connection(server, sock); + server->backup_noswitch = FALSE; } break; @@ -2073,7 +2617,6 @@ void silc_server_packet_parse_type(SilcServer server, * one protocol for connection executing at once hence this * success message is for whatever protocol is executing currently. */ - SILC_LOG_DEBUG(("Success packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; if (sock->protocol) @@ -2086,24 +2629,52 @@ void silc_server_packet_parse_type(SilcServer server, * one protocol for connection executing at once hence this * failure message is for whatever protocol is executing currently. */ - SILC_LOG_DEBUG(("Failure packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; + + /* Check for failure START_USE from backup router */ + if (server->server_type == SILC_SERVER && + server->backup_primary && packet->buffer->len == 4) { + SilcUInt32 type; + SILC_GET32_MSB(type, packet->buffer->data); + if (type == SILC_SERVER_BACKUP_START_USE) { + /* Attempt to reconnect to primary */ + SILC_LOG_DEBUG(("Received failed START_USE from backup %s", sock->ip)); + + /* Default action is to disconnect from backup and reconnect to + primary. Since this failure can happen during switching to + backup (backup might have not noticed the primary going down yet), + we will wait a while and keep sending START_USE to backup. + Only after that we'll give up. */ + if (server->router == sock->user_data && + (time(0) - server->router_connect) < 30) { + SILC_LOG_DEBUG(("Resending START_USE to backup router")); + silc_server_backup_send_start_use(server, sock, FALSE); + break; + } + + /* If backup is our primary, disconnect now. */ + if (server->router == sock->user_data) { + if (sock->user_data) + silc_server_free_sock_user_data(server, sock, NULL); + SILC_SET_DISCONNECTING(sock); + silc_server_close_connection(server, sock); + } + + /* Reconnect */ + silc_server_create_connections(server); + } + } + + /* Execute protocol */ if (sock->protocol) { - SilcServerFailureContext f; - f = silc_calloc(1, sizeof(*f)); - f->server = server; - f->sock = sock; - - /* We will wait 5 seconds to process this failure packet */ - silc_schedule_task_add(server->schedule, sock->sock, - silc_server_failure_callback, (void *)f, 5, 0, - SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE; + silc_protocol_execute(sock->protocol, server->schedule, 0, 0); + break; } break; case SILC_PACKET_REJECT: - SILC_LOG_DEBUG(("Reject packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; return; @@ -2114,7 +2685,6 @@ void silc_server_packet_parse_type(SilcServer server, * Received notify packet. Server can receive notify packets from * router. Server then relays the notify messages to clients if needed. */ - SILC_LOG_DEBUG(("Notify packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) silc_server_notify_list(server, sock, packet); else @@ -2130,7 +2700,6 @@ void silc_server_packet_parse_type(SilcServer server, * (although probably most common ones) thus they are handled * specially. */ - SILC_LOG_DEBUG(("Channel Message packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; idata->last_receive = time(NULL); @@ -2144,7 +2713,6 @@ void silc_server_packet_parse_type(SilcServer server, * locally connected clients on the particular channel. Router * never receives this channel and thus is ignored. */ - SILC_LOG_DEBUG(("Channel Key packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_channel_key(server, sock, packet); @@ -2158,7 +2726,6 @@ void silc_server_packet_parse_type(SilcServer server, * Recived command. Processes the command request and allocates the * command context and calls the command. */ - SILC_LOG_DEBUG(("Command packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_command_process(server, sock, packet); @@ -2170,7 +2737,6 @@ void silc_server_packet_parse_type(SilcServer server, * may be reply to command sent by us or reply to command sent by client * that we've routed further. */ - SILC_LOG_DEBUG(("Command Reply packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_command_reply(server, sock, packet); @@ -2184,7 +2750,6 @@ void silc_server_packet_parse_type(SilcServer server, * Received private message packet. The packet is coming from either * client or server. */ - SILC_LOG_DEBUG(("Private Message packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; idata->last_receive = time(NULL); @@ -2204,7 +2769,6 @@ void silc_server_packet_parse_type(SilcServer server, * Key Exchange protocol packets */ case SILC_PACKET_KEY_EXCHANGE: - SILC_LOG_DEBUG(("KE packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; @@ -2219,12 +2783,16 @@ void silc_server_packet_parse_type(SilcServer server, silc_protocol_execute(sock->protocol, server->schedule, 0, 100000); } else { SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange " - "protocol active, packet dropped.")); + "protocol active (%s:%d [%s]).", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); } break; case SILC_PACKET_KEY_EXCHANGE_1: - SILC_LOG_DEBUG(("KE 1 packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; @@ -2263,12 +2831,16 @@ void silc_server_packet_parse_type(SilcServer server, } } else { SILC_LOG_ERROR(("Received Key Exchange 1 packet but no key exchange " - "protocol active, packet dropped.")); + "protocol active (%s:%d [%s]).", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); } break; case SILC_PACKET_KEY_EXCHANGE_2: - SILC_LOG_DEBUG(("KE 2 packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; @@ -2307,7 +2879,12 @@ void silc_server_packet_parse_type(SilcServer server, } } else { SILC_LOG_ERROR(("Received Key Exchange 2 packet but no key exchange " - "protocol active, packet dropped.")); + "protocol active (%s:%d [%s]).", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); } break; @@ -2318,7 +2895,6 @@ void silc_server_packet_parse_type(SilcServer server, * authentication method for the connection. This packet maybe received * at any time. */ - SILC_LOG_DEBUG(("Connection authentication request packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_connection_auth_request(server, sock, packet); @@ -2330,7 +2906,6 @@ void silc_server_packet_parse_type(SilcServer server, case SILC_PACKET_CONNECTION_AUTH: /* Start of the authentication protocol. We receive here the authentication data and will verify it. */ - SILC_LOG_DEBUG(("Connection auth packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; @@ -2346,7 +2921,12 @@ void silc_server_packet_parse_type(SilcServer server, silc_protocol_execute(sock->protocol, server->schedule, 0, 0); } else { SILC_LOG_ERROR(("Received Connection Auth packet but no authentication " - "protocol active, packet dropped.")); + "protocol active (%s:%d [%s]).", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); } break; @@ -2357,7 +2937,6 @@ void silc_server_packet_parse_type(SilcServer server, * to distribute information about new registered entities in the * SILC network. */ - SILC_LOG_DEBUG(("New ID packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) silc_server_new_id_list(server, sock, packet); else @@ -2370,7 +2949,6 @@ void silc_server_packet_parse_type(SilcServer server, * we will use to create initial client ID. After creating new * ID we will send it to the client. */ - SILC_LOG_DEBUG(("New Client packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_new_client(server, sock, packet); @@ -2382,7 +2960,6 @@ void silc_server_packet_parse_type(SilcServer server, * information that we may save. This is received after server has * connected to us. */ - SILC_LOG_DEBUG(("New Server packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_new_server(server, sock, packet); @@ -2393,7 +2970,6 @@ void silc_server_packet_parse_type(SilcServer server, * Received new channel packet. Information about new channel in the * network are distributed using this packet. */ - SILC_LOG_DEBUG(("New Channel packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) silc_server_new_channel_list(server, sock, packet); else @@ -2404,7 +2980,6 @@ void silc_server_packet_parse_type(SilcServer server, /* * Received heartbeat. */ - SILC_LOG_DEBUG(("Heartbeat packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; break; @@ -2413,7 +2988,6 @@ void silc_server_packet_parse_type(SilcServer server, /* * Received heartbeat. */ - SILC_LOG_DEBUG(("Key agreement packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_key_agreement(server, sock, packet); @@ -2424,7 +2998,6 @@ void silc_server_packet_parse_type(SilcServer server, * Received re-key packet. The sender wants to regenerate the session * keys. */ - SILC_LOG_DEBUG(("Re-key packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_rekey(server, sock, packet); @@ -2434,7 +3007,6 @@ void silc_server_packet_parse_type(SilcServer server, /* * The re-key is done. */ - SILC_LOG_DEBUG(("Re-key done packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; @@ -2453,13 +3025,17 @@ void silc_server_packet_parse_type(SilcServer server, silc_protocol_execute(sock->protocol, server->schedule, 0, 0); } else { SILC_LOG_ERROR(("Received Re-key done packet but no re-key " - "protocol active, packet dropped.")); + "protocol active (%s:%d [%s]).", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); } break; case SILC_PACKET_FTP: /* FTP packet */ - SILC_LOG_DEBUG(("FTP packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_ftp(server, sock, packet); @@ -2467,7 +3043,6 @@ void silc_server_packet_parse_type(SilcServer server, 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); @@ -2476,7 +3051,6 @@ void silc_server_packet_parse_type(SilcServer server, case SILC_PACKET_RESUME_ROUTER: /* Resume router packet received. This packet is received for backup router resuming protocol. */ - SILC_LOG_DEBUG(("Resume router packet")); if (packet->flags & SILC_PACKET_FLAG_LIST) break; silc_server_backup_resume_router(server, sock, packet); @@ -2486,7 +3060,6 @@ void silc_server_packet_parse_type(SilcServer server, SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type)); break; } - } /* Creates connection to a remote router. */ @@ -2498,7 +3071,6 @@ void silc_server_create_connection(SilcServer server, /* Allocate connection object for hold connection specific stuff. */ sconn = silc_calloc(1, sizeof(*sconn)); - sconn->server = server; sconn->remote_host = strdup(remote_host); sconn->remote_port = port; sconn->no_reconnect = TRUE; @@ -2511,57 +3083,72 @@ void silc_server_create_connection(SilcServer server, SILC_TASK_CALLBACK(silc_server_close_connection_final) { - silc_socket_free((SilcSocketConnection)context); -} + SilcServer server = app_context; + SilcSocketConnection sock = context; -/* Closes connection to socket connection */ - -void silc_server_close_connection(SilcServer server, - SilcSocketConnection sock) -{ - if (!server->sockets[sock->sock]) - return; + SILC_LOG_DEBUG(("Deleting socket %p", sock)); - SILC_LOG_INFO(("Closing connection %s:%d [%s]", sock->hostname, - sock->port, - (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : - sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : - sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : - "Router"))); + /* Close the actual connection */ + silc_net_close_connection(sock->sock); + server->sockets[sock->sock] = NULL; /* We won't listen for this connection anymore */ + silc_schedule_task_del_by_fd(server->schedule, sock->sock); silc_schedule_unset_listen_fd(server->schedule, sock->sock); - /* Unregister all tasks */ - silc_schedule_task_del_by_fd(server->schedule, sock->sock); + silc_socket_free(sock); +} + +/* Closes connection to socket connection */ + +void silc_server_close_connection(SilcServer server, + SilcSocketConnection sock) +{ + char tmp[128]; - /* Close the actual connection */ - silc_net_close_connection(sock->sock); - server->sockets[sock->sock] = NULL; + if (SILC_IS_DISCONNECTED(sock)) { + silc_schedule_task_del_by_fd(server->schedule, sock->sock); + silc_schedule_task_add(server->schedule, sock->sock, + silc_server_close_connection_final, + (void *)sock, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + server->sockets[sock->sock] = NULL; + return; + } - /* If sock->user_data is NULL then we'll check for active protocols - here since the silc_server_free_sock_user_data has not been called - for this connection. */ - if (!sock->user_data) { - /* If any protocol is active cancel its execution. It will call - the final callback which will finalize the disconnection. */ - if (sock->protocol) { - silc_protocol_cancel(sock->protocol, server->schedule); - sock->protocol->state = SILC_PROTOCOL_STATE_ERROR; - silc_protocol_execute_final(sock->protocol, server->schedule); - sock->protocol = NULL; - return; - } + /* If any protocol is active cancel its execution. It will call + the final callback which will finalize the disconnection. */ + if (sock->protocol && sock->protocol->protocol && + sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) { + SILC_LOG_DEBUG(("Cancelling protocol, calling final callback")); + silc_protocol_cancel(sock->protocol, server->schedule); + sock->protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute_final(sock->protocol, server->schedule); + sock->protocol = NULL; + return; } - silc_schedule_task_add(server->schedule, 0, + memset(tmp, 0, sizeof(tmp)); + silc_socket_get_error(sock, tmp, sizeof(tmp)); + SILC_LOG_INFO(("Closing connection %s:%d [%s] %s", sock->hostname, + sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"), tmp[0] ? tmp : "")); + + SILC_SET_DISCONNECTED(sock); + silc_schedule_task_add(server->schedule, sock->sock, silc_server_close_connection_final, (void *)sock, 0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + server->sockets[sock->sock] = NULL; } /* Sends disconnect message to remote connection and disconnects the - connection. */ + connection. NOTE: If this is called from protocol callback + then sock->protocol must be set NULL before calling this, since + this routine dispatches protocol callbacks too. */ void silc_server_disconnect_remote(SilcServer server, SilcSocketConnection sock, @@ -2576,6 +3163,12 @@ void silc_server_disconnect_remote(SilcServer server, if (!sock) return; + if (SILC_IS_DISCONNECTING(sock)) { + SILC_SET_DISCONNECTED(sock); + silc_server_close_connection(server, sock); + return; + } + memset(buf, 0, sizeof(buf)); va_start(ap, status); cp = va_arg(ap, char *); @@ -2610,22 +3203,19 @@ void silc_server_disconnect_remote(SilcServer server, silc_server_packet_queue_purge(server, sock); /* Mark the connection to be disconnected */ - SILC_SET_DISCONNECTED(sock); + SILC_SET_DISCONNECTING(sock); silc_server_close_connection(server, sock); } -typedef struct { - SilcServer server; - SilcClientEntry client; -} *FreeClientInternal; - SILC_TASK_CALLBACK(silc_server_free_client_data_timeout) { - FreeClientInternal i = (FreeClientInternal)context; + SilcServer server = app_context; + SilcClientEntry client = context; + + assert(!silc_hash_table_count(client->channels)); - silc_idlist_del_data(i->client); - silc_idcache_purge_by_context(i->server->local_list->clients, i->client); - silc_free(i); + silc_idlist_del_data(client); + silc_idcache_purge_by_context(server->local_list->clients, client); } /* Frees client data and notifies about client's signoff. */ @@ -2636,7 +3226,7 @@ void silc_server_free_client_data(SilcServer server, int notify, const char *signoff) { - FreeClientInternal i = silc_calloc(1, sizeof(*i)); + SILC_LOG_DEBUG(("Freeing client data")); /* If there is pending outgoing data for the client then purge it to the network before removing the client entry. */ @@ -2649,23 +3239,23 @@ void silc_server_free_client_data(SilcServer server, 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); - - /* 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); + silc_server_send_notify_signoff(server, SILC_PRIMARY_ROUTE(server), + SILC_BROADCAST(server), client->id, + signoff); } + /* Remove client from all channels */ + if (notify) + silc_server_remove_from_channels(server, NULL, client, + TRUE, (char *)signoff, TRUE, FALSE); + else + silc_server_remove_from_channels(server, NULL, client, + FALSE, NULL, FALSE, 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--; @@ -2676,17 +3266,23 @@ void silc_server_free_client_data(SilcServer server, 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 */ - i->server = server; - i->client = client; - silc_schedule_task_add(server->schedule, 0, - silc_server_free_client_data_timeout, - (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; + into history (for WHOWAS command) for 5 minutes, unless we're + shutting down server. */ + if (!server->server_shutdown) { + silc_schedule_task_add(server->schedule, 0, + silc_server_free_client_data_timeout, + client, 300, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); + client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; + client->data.status &= ~SILC_IDLIST_STATUS_LOCAL; + client->mode = 0; + client->router = NULL; + client->connection = NULL; + } else { + /* Delete directly since we're shutting down server */ + silc_idlist_del_data(client); + silc_idlist_del_client(server->local_list, client); + } } /* Frees user_data pointer from socket connection object. This also sends @@ -2697,7 +3293,16 @@ void silc_server_free_sock_user_data(SilcServer server, SilcSocketConnection sock, const char *signoff_message) { - SILC_LOG_DEBUG(("Start")); + + /* If any protocol is active cancel its execution */ + if (sock->protocol && sock->protocol->protocol && + sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) { + SILC_LOG_DEBUG(("Cancelling protocol, calling final callback")); + silc_protocol_cancel(sock->protocol, server->schedule); + sock->protocol->state = SILC_PROTOCOL_STATE_ERROR; + silc_protocol_execute_final(sock->protocol, server->schedule); + sock->protocol = NULL; + } switch (sock->type) { case SILC_SOCKET_TYPE_CLIENT: @@ -2713,45 +3318,56 @@ void silc_server_free_sock_user_data(SilcServer server, SilcServerEntry user_data = (SilcServerEntry)sock->user_data; SilcServerEntry backup_router = NULL; + SILC_LOG_DEBUG(("Freeing server data")); + if (user_data->id) backup_router = silc_server_backup_get(server, user_data->id); + if (!server->backup_router && server->server_type == SILC_ROUTER && + backup_router == server->id_entry && + sock->type != SILC_SOCKET_TYPE_ROUTER) + backup_router = NULL; + + if (server->server_shutdown || server->backup_noswitch) + backup_router = NULL; + /* If this was our primary router connection then we're lost to the outside world. */ if (server->router == user_data) { /* Check whether we have a backup router connection */ if (!backup_router || backup_router == user_data) { - silc_schedule_task_add(server->schedule, 0, - silc_server_connect_to_router, - server, 1, 0, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); - + if (!server->no_reconnect) + silc_server_create_connections(server); server->id_entry->router = NULL; server->router = NULL; server->standalone = TRUE; + server->backup_primary = FALSE; backup_router = NULL; } else { - SILC_LOG_INFO(("New primary router is backup router %s", - backup_router->server_name)); - SILC_LOG_DEBUG(("New primary router is backup router %s", - backup_router->server_name)); -#ifdef BACKUP_SINGLE_ROUTER if (server->id_entry != backup_router) { -#endif /* BACKUP_SINGLE_ROUTER */ + SILC_LOG_INFO(("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 + backup_router->data.status &= ~SILC_IDLIST_STATUS_DISABLED; + + /* Send START_USE to backup router to indicate we have switched */ + silc_server_backup_send_start_use(server, + backup_router->connection, + FALSE); } else { + SILC_LOG_INFO(("We are now new primary router in this cell")); server->id_entry->router = NULL; server->router = NULL; server->standalone = TRUE; } -#endif /* BACKUP_SINGLE_ROUTER */ - if (server->server_type == SILC_BACKUP_ROUTER) { + /* We stop here to take a breath */ + sleep(2); + + if (server->backup_router) { server->server_type = SILC_ROUTER; /* We'll need to constantly try to reconnect to the primary @@ -2768,32 +3384,88 @@ void silc_server_free_sock_user_data(SilcServer server, } else if (backup_router) { SILC_LOG_INFO(("Enabling the use of backup router %s", backup_router->server_name)); - SILC_LOG_DEBUG(("Enabling the use of backup router %s", - backup_router->server_name)); /* Mark this connection as replaced */ silc_server_backup_replaced_add(server, user_data->id, backup_router); + } else if (server->server_type == SILC_SERVER && + sock->type == SILC_SOCKET_TYPE_ROUTER) { + /* Reconnect to the router (backup) */ + if (!server->no_reconnect) + silc_server_create_connections(server); } + if (user_data->server_name) + SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, SILC_NOTIFY_TYPE_NONE, + ("Server %s signoff", user_data->server_name)); + if (!backup_router) { - /* Free all client entries that this server owns as they will - become invalid now as well. */ - if (user_data->id) - silc_server_remove_clients_by_server(server, user_data, TRUE); + /* Remove all servers that are originated from this server, and + remove the clients of those servers too. */ + silc_server_remove_servers_by_server(server, user_data, TRUE); + +#if 0 + /* Remove the clients that this server owns as they will become + invalid now too. For backup router the server is actually + coming from the primary router, so mark that as the owner + of this entry. */ + if (server->server_type == SILC_BACKUP_ROUTER && + sock->type == SILC_SOCKET_TYPE_SERVER) + silc_server_remove_clients_by_server(server, server->router, + user_data, TRUE); + else +#endif + silc_server_remove_clients_by_server(server, user_data, + user_data, TRUE); + + /* Remove channels owned by this server */ if (server->server_type == SILC_SERVER) silc_server_remove_channels_by_server(server, user_data); } else { + /* Enable local server connections that may be disabled */ + silc_server_local_servers_toggle_enabled(server, TRUE); + /* Update the client entries of this server to the new backup - router. This also removes the clients that *really* was owned - by the primary router and went down with the router. */ - silc_server_update_clients_by_server(server, user_data, backup_router, - TRUE, TRUE); + router. If we are the backup router we also resolve the real + servers for the clients. After updating is over this also + removes the clients that this server explicitly owns. */ + silc_server_update_clients_by_server(server, user_data, + backup_router, TRUE); + + /* If we are router and just lost our primary router (now standlaone) + we remove everything that was behind it, since we don't know + any better. */ + if (server->server_type == SILC_ROUTER && server->standalone) + /* Remove all servers that are originated from this server, and + remove the clients of those servers too. */ + silc_server_remove_servers_by_server(server, user_data, TRUE); + + /* Finally remove the clients that are explicitly owned by this + server. They go down with the server. */ + silc_server_remove_clients_by_server(server, user_data, + user_data, TRUE); + + /* Update our server cache to use the new backup router too. */ silc_server_update_servers_by_server(server, user_data, backup_router); if (server->server_type == SILC_SERVER) silc_server_update_channels_by_server(server, user_data, backup_router); + + /* Send notify about primary router going down to local operators */ + if (server->backup_router) + SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, + SILC_NOTIFY_TYPE_NONE, + ("%s switched to backup router %s " + "(we are primary router now)", + server->server_name, server->server_name)); + else if (server->router) + SILC_SERVER_SEND_OPERS(server, FALSE, TRUE, + SILC_NOTIFY_TYPE_NONE, + ("%s switched to backup router %s", + server->server_name, + server->router->server_name)); } + server->backup_noswitch = FALSE; /* Free the server entry */ silc_server_backup_del(server, user_data); @@ -2811,7 +3483,7 @@ void silc_server_free_sock_user_data(SilcServer server, if (server->server_type == SILC_ROUTER) server->stat.cell_servers--; - if (backup_router) { + if (backup_router && backup_router != server->id_entry) { /* Announce all of our stuff that was created about 5 minutes ago. The backup router knows all the other stuff already. */ if (server->server_type == SILC_ROUTER) @@ -2830,20 +3502,14 @@ void silc_server_free_sock_user_data(SilcServer server, { SilcUnknownEntry user_data = (SilcUnknownEntry)sock->user_data; + SILC_LOG_DEBUG(("Freeing unknown connection data")); + silc_idlist_del_data(user_data); silc_free(user_data); break; } } - /* If any protocol is active cancel its execution */ - if (sock->protocol) { - silc_protocol_cancel(sock->protocol, server->schedule); - sock->protocol->state = SILC_PROTOCOL_STATE_ERROR; - silc_protocol_execute_final(sock->protocol, server->schedule); - sock->protocol = NULL; - } - sock->user_data = NULL; } @@ -2856,22 +3522,29 @@ void silc_server_remove_from_channels(SilcServer server, SilcClientEntry client, bool notify, const char *signoff_message, - bool keygen) + bool keygen, + bool killed) { SilcChannelEntry channel; SilcChannelClientEntry chl; SilcHashTableList htl; - SilcBuffer clidp; - - SILC_LOG_DEBUG(("Start")); + SilcBuffer clidp = NULL; - if (!client || !client->id) + if (!client) return; - clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); - if (!clidp) + if (notify && !client->id) notify = FALSE; + SILC_LOG_DEBUG(("Removing client %s from joined channels", + notify ? silc_id_render(client->id, SILC_ID_CLIENT) : "")); + + if (notify) { + 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. */ silc_hash_table_list(client->channels, &htl); @@ -2880,26 +3553,27 @@ void silc_server_remove_from_channels(SilcServer server, /* Remove channel if this is last client leaving the channel, unless the channel is permanent. */ - if (server->server_type == SILC_ROUTER && + if (server->server_type != SILC_SERVER && silc_hash_table_count(channel->user_list) < 2) { silc_server_channel_delete(server, channel); continue; } silc_hash_table_del(client->channels, channel); - silc_hash_table_del(channel->user_list, chl->client); + silc_hash_table_del(channel->user_list, client); channel->user_count--; /* If there is no global users on the channel anymore mark the channel as local channel. Do not check if the removed client is local client. */ - if (server->server_type != SILC_ROUTER && channel->global_users && + if (server->server_type == SILC_SERVER && channel->global_users && chl->client->router && !silc_server_channel_has_global(channel)) channel->global_users = FALSE; + memset(chl, 'A', sizeof(*chl)); silc_free(chl); /* Update statistics */ - if (client->connection) + if (SILC_IS_LOCAL(client)) server->stat.my_chanclients--; if (server->server_type == SILC_ROUTER) { server->stat.cell_chanclients--; @@ -2909,11 +3583,11 @@ void silc_server_remove_from_channels(SilcServer server, /* 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, unless the channel is permanent channel */ - if (server->server_type != SILC_ROUTER && + if (server->server_type == SILC_SERVER && !silc_server_channel_has_local(channel)) { /* Notify about leaving client if this channel has global users. */ if (notify && channel->global_users) - silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE, SILC_NOTIFY_TYPE_SIGNOFF, signoff_message ? 2 : 1, clidp->data, clidp->len, @@ -2927,13 +3601,32 @@ void silc_server_remove_from_channels(SilcServer server, /* Send notify to channel about client leaving SILC and channel too */ if (notify) - silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE, SILC_NOTIFY_TYPE_SIGNOFF, signoff_message ? 2 : 1, clidp->data, clidp->len, signoff_message, signoff_message ? strlen(signoff_message) : 0); + if (killed && clidp) { + /* Remove the client from channel's invite list */ + if (channel->invite_list && + silc_hash_table_count(channel->invite_list)) { + SilcBuffer ab; + SilcArgumentPayload iargs; + ab = silc_argument_payload_encode_one(NULL, clidp->data, + clidp->len, 3); + iargs = silc_argument_payload_parse(ab->data, ab->len, 1); + silc_server_inviteban_process(server, channel->invite_list, 1, iargs); + silc_buffer_free(ab); + silc_argument_payload_free(iargs); + } + } + + /* Don't create keys if we are shutting down */ + if (server->server_shutdown) + continue; + /* Re-generate channel key if needed */ if (keygen && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) { if (!silc_server_create_channel_key(server, channel, 0)) @@ -2948,7 +3641,8 @@ void silc_server_remove_from_channels(SilcServer server, } silc_hash_table_list_reset(&htl); - silc_buffer_free(clidp); + if (clidp) + silc_buffer_free(clidp); } /* Removes client from one channel. This is used for example when client @@ -2967,7 +3661,7 @@ bool silc_server_remove_from_one_channel(SilcServer server, SilcBuffer clidp; SILC_LOG_DEBUG(("Removing %s from channel %s", - silc_id_render(client->id, SILC_ID_CLIENT), + 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 @@ -2977,26 +3671,27 @@ bool silc_server_remove_from_one_channel(SilcServer server, /* Remove channel if this is last client leaving the channel, unless the channel is permanent. */ - if (server->server_type == SILC_ROUTER && + if (server->server_type != SILC_SERVER && silc_hash_table_count(channel->user_list) < 2) { silc_server_channel_delete(server, channel); return FALSE; } - silc_hash_table_del(client->channels, chl->channel); - silc_hash_table_del(channel->user_list, chl->client); + silc_hash_table_del(client->channels, channel); + silc_hash_table_del(channel->user_list, client); channel->user_count--; /* If there is no global users on the channel anymore mark the channel as local channel. Do not check if the client is local client. */ - if (server->server_type != SILC_ROUTER && channel->global_users && + if (server->server_type == SILC_SERVER && channel->global_users && chl->client->router && !silc_server_channel_has_global(channel)) channel->global_users = FALSE; + memset(chl, 'O', sizeof(*chl)); silc_free(chl); /* Update statistics */ - if (client->connection) + if (SILC_IS_LOCAL(client)) server->stat.my_chanclients--; if (server->server_type == SILC_ROUTER) { server->stat.cell_chanclients--; @@ -3010,11 +3705,11 @@ bool silc_server_remove_from_one_channel(SilcServer server, /* 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, unless the channel is permanent channel */ - if (server->server_type != SILC_ROUTER && + if (server->server_type == SILC_SERVER && !silc_server_channel_has_local(channel)) { /* Notify about leaving client if this channel has global users. */ if (notify && channel->global_users) - silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE, SILC_NOTIFY_TYPE_LEAVE, 1, clidp->data, clidp->len); @@ -3026,7 +3721,7 @@ bool silc_server_remove_from_one_channel(SilcServer server, /* Send notify to channel about client leaving the channel */ if (notify) - silc_server_send_notify_to_channel(server, NULL, channel, FALSE, + silc_server_send_notify_to_channel(server, NULL, channel, FALSE, TRUE, SILC_NOTIFY_TYPE_LEAVE, 1, clidp->data, clidp->len); @@ -3054,7 +3749,8 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote) /* 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) { + if (sock->protocol && sock->protocol->protocol && + sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) { protocol = sock->protocol->protocol->type; silc_protocol_cancel(sock->protocol, server->schedule); sock->protocol->state = SILC_PROTOCOL_STATE_ERROR; @@ -3063,15 +3759,15 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote) return; } - if (sock->user_data) - silc_server_free_sock_user_data(server, sock, NULL); - - silc_server_disconnect_remote(server, sock, - protocol == + 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"); + + if (sock->user_data) + silc_server_free_sock_user_data(server, sock, NULL); } /* Creates new channel. Sends NEW_CHANNEL packet to primary route. This @@ -3091,7 +3787,7 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, SilcCipher key; SilcHmac newhmac; - SILC_LOG_DEBUG(("Creating new channel")); + SILC_LOG_DEBUG(("Creating new channel %s", channel_name)); if (!cipher) cipher = SILC_DEFAULT_CIPHER; @@ -3143,8 +3839,8 @@ SilcChannelEntry silc_server_create_new_channel(SilcServer server, /* Notify other routers about the new channel. We send the packet to our primary route. */ - if (broadcast && server->standalone == FALSE) - silc_server_send_new_channel(server, server->router->connection, TRUE, + if (broadcast) + silc_server_send_new_channel(server, SILC_PRIMARY_ROUTE(server), TRUE, channel_name, entry->id, silc_id_get_len(entry->id, SILC_ID_CHANNEL), entry->mode); @@ -3189,7 +3885,7 @@ silc_server_create_new_channel_with_id(SilcServer server, SilcCipher key; SilcHmac newhmac; - SILC_LOG_DEBUG(("Creating new channel")); + SILC_LOG_DEBUG(("Creating new channel %s", channel_name)); if (!cipher) cipher = SILC_DEFAULT_CIPHER; @@ -3228,8 +3924,8 @@ silc_server_create_new_channel_with_id(SilcServer server, /* Notify other routers about the new channel. We send the packet to our primary route. */ - if (broadcast && server->standalone == FALSE) - silc_server_send_new_channel(server, server->router->connection, TRUE, + if (broadcast) + silc_server_send_new_channel(server, SILC_PRIMARY_ROUTE(server), TRUE, channel_name, entry->id, silc_id_get_len(entry->id, SILC_ID_CHANNEL), entry->mode); @@ -3264,11 +3960,15 @@ silc_server_create_new_channel_with_id(SilcServer server, SILC_TASK_CALLBACK(silc_server_channel_key_rekey) { + SilcServer server = app_context; SilcServerChannelRekey rekey = (SilcServerChannelRekey)context; - SilcServer server = (SilcServer)rekey->context; rekey->task = NULL; + /* Return now if we are shutting down */ + if (server->server_shutdown) + return; + if (!silc_server_create_channel_key(server, rekey->channel, rekey->key_len)) return; @@ -3287,13 +3987,13 @@ bool silc_server_create_channel_key(SilcServer server, unsigned char channel_key[32], hash[32]; SilcUInt32 len; - SILC_LOG_DEBUG(("Generating channel key")); - if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) { SILC_LOG_DEBUG(("Channel has private keys, will not generate new key")); return TRUE; } + SILC_LOG_DEBUG(("Generating channel %s key", channel->channel_name)); + if (!channel->channel_key) if (!silc_cipher_alloc(SILC_DEFAULT_CIPHER, &channel->channel_key)) { channel->channel_key = NULL; @@ -3326,7 +4026,12 @@ bool silc_server_create_channel_key(SilcServer server, /* Generate HMAC key from the channel key data and set it */ if (!channel->hmac) - silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac); + if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) { + memset(channel->key, 0, channel->key_len / 8); + silc_free(channel->key); + channel->channel_key = NULL; + return FALSE; + } silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key, len, hash); silc_hmac_set_key(channel->hmac, hash, silc_hash_len(silc_hmac_get_hash(channel->hmac))); @@ -3335,7 +4040,6 @@ bool silc_server_create_channel_key(SilcServer server, if (server->server_type == SILC_ROUTER) { if (!channel->rekey) channel->rekey = silc_calloc(1, sizeof(*channel->rekey)); - channel->rekey->context = (void *)server; channel->rekey->channel = channel; channel->rekey->key_len = key_len; if (channel->rekey->task) @@ -3367,8 +4071,6 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, SilcUInt32 tmp_len; char *cipher; - SILC_LOG_DEBUG(("Start")); - /* Decode channel key payload */ payload = silc_channel_key_payload_parse(key_payload->data, key_payload->len); @@ -3393,13 +4095,16 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, if (!channel) { channel = silc_idlist_find_channel_by_id(server->global_list, id, NULL); if (!channel) { - SILC_LOG_ERROR(("Received key for non-existent channel %s", - silc_id_render(id, SILC_ID_CHANNEL))); + if (server->server_type == SILC_ROUTER) + SILC_LOG_ERROR(("Received key for non-existent channel %s", + silc_id_render(id, SILC_ID_CHANNEL))); goto out; } } } + SILC_LOG_DEBUG(("Saving new channel %s key", channel->channel_name)); + tmp = silc_channel_key_get_key(payload, &tmp_len); if (!tmp) { channel = NULL; @@ -3437,7 +4142,12 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, /* Generate HMAC key from the channel key data and set it */ if (!channel->hmac) - silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac); + if (!silc_hmac_alloc(SILC_DEFAULT_HMAC, NULL, &channel->hmac)) { + memset(channel->key, 0, channel->key_len / 8); + silc_free(channel->key); + channel->channel_key = NULL; + return FALSE; + } silc_hash_make(silc_hmac_get_hash(channel->hmac), tmp, tmp_len, hash); silc_hmac_set_key(channel->hmac, hash, silc_hash_len(silc_hmac_get_hash(channel->hmac))); @@ -3448,7 +4158,6 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, if (server->server_type == SILC_ROUTER) { if (!channel->rekey) channel->rekey = silc_calloc(1, sizeof(*channel->rekey)); - channel->rekey->context = (void *)server; channel->rekey->channel = channel; if (channel->rekey->task) silc_schedule_task_del(server->schedule, channel->rekey->task); @@ -3477,12 +4186,13 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, void silc_server_perform_heartbeat(SilcSocketConnection sock, void *hb_context) { - SilcServerHBContext hb = (SilcServerHBContext)hb_context; + SilcServer server = hb_context; - SILC_LOG_DEBUG(("Sending heartbeat to %s (%s)", sock->hostname, sock->ip)); + SILC_LOG_DEBUG(("Sending heartbeat to %s:%d (%s)", sock->hostname, + sock->port, sock->ip)); /* Send the heartbeat */ - silc_server_send_heartbeat(hb->server, sock); + silc_server_send_heartbeat(server, sock); } /* Returns assembled of all servers in the given ID list. The packet's @@ -3612,6 +4322,12 @@ static void silc_server_announce_get_clients(SilcServer server, break; continue; } + if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) && + !client->connection && !client->router && !SILC_IS_LOCAL(client)) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + continue; + } idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); @@ -3624,9 +4340,10 @@ static void silc_server_announce_get_clients(SilcServer server, silc_buffer_pull(*clients, idp->len); SILC_PUT32_MSB(client->mode, mode); - tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE, - 2, idp->data, idp->len, - mode, 4); + tmp = + silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE, + 2, idp->data, idp->len, + mode, 4); *umodes = silc_buffer_realloc(*umodes, (*umodes ? (*umodes)->truelen + tmp->len : @@ -3713,6 +4430,66 @@ void silc_server_announce_get_channel_topic(SilcServer server, } } +/* Returns channel's invite and ban lists */ + +void silc_server_announce_get_inviteban(SilcServer server, + SilcChannelEntry channel, + SilcBuffer *invite, + SilcBuffer *ban) +{ + SilcBuffer list, idp, idp2, tmp2; + SilcUInt32 type; + SilcHashTableList htl; + const unsigned char a[1] = { 0x03 }; + + idp = silc_id_payload_encode((void *)channel->id, SILC_ID_CHANNEL); + + /* Encode invite list */ + if (channel->invite_list && silc_hash_table_count(channel->invite_list)) { + list = silc_buffer_alloc_size(2); + type = silc_hash_table_count(channel->invite_list); + SILC_PUT16_MSB(type, list->data); + silc_hash_table_list(channel->invite_list, &htl); + while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) + list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len, + type); + silc_hash_table_list_reset(&htl); + + idp2 = silc_id_payload_encode(server->id, SILC_ID_SERVER); + *invite = + silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_INVITE, 5, + idp->data, idp->len, + channel->channel_name, + strlen(channel->channel_name), + idp2->data, idp2->len, + a, 1, + list->data, list->len); + silc_buffer_free(idp2); + silc_buffer_free(list); + } + + /* Encode ban list */ + if (channel->ban_list && silc_hash_table_count(channel->ban_list)) { + list = silc_buffer_alloc_size(2); + type = silc_hash_table_count(channel->ban_list); + SILC_PUT16_MSB(type, list->data); + silc_hash_table_list(channel->ban_list, &htl); + while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) + list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len, + type); + silc_hash_table_list_reset(&htl); + + *ban = + silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_BAN, 3, + idp->data, idp->len, + a, 1, + list->data, list->len); + silc_buffer_free(list); + } + + silc_buffer_free(idp); +} + /* Returns assembled packets for channel users of the `channel'. */ void silc_server_announce_get_channel_users(SilcServer server, @@ -3724,32 +4501,35 @@ void silc_server_announce_get_channel_users(SilcServer server, SilcChannelClientEntry chl; SilcHashTableList htl; SilcBuffer chidp, clidp, csidp; - SilcBuffer tmp; + SilcBuffer tmp, fkey = NULL, chpklist; int len; - unsigned char mode[4], *fkey = NULL; - SilcUInt32 fkey_len = 0; + unsigned char mode[4]; char *hmac; SILC_LOG_DEBUG(("Start")); chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); csidp = silc_id_payload_encode(server->id, SILC_ID_SERVER); + chpklist = silc_server_get_channel_pk_list(server, channel, TRUE, FALSE); /* 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 = + fkey = silc_pkcs_public_key_payload_encode(channel->founder_key); + tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE, - 6, csidp->data, csidp->len, + 7, 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); + fkey ? fkey->data : NULL, + fkey ? fkey->len : 0, + chpklist ? chpklist->data : NULL, + chpklist ? chpklist->len : 0); len = tmp->len; *channel_modes = silc_buffer_realloc(*channel_modes, @@ -3761,9 +4541,8 @@ void silc_server_announce_get_channel_users(SilcServer server, silc_buffer_put(*channel_modes, tmp->data, tmp->len); silc_buffer_pull(*channel_modes, len); silc_buffer_free(tmp); - silc_free(fkey); + silc_buffer_free(fkey); fkey = NULL; - fkey_len = 0; /* Now find all users on the channel */ silc_hash_table_list(channel->user_list, &htl); @@ -3790,12 +4569,13 @@ void silc_server_announce_get_channel_users(SilcServer server, /* 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); + fkey = silc_pkcs_public_key_payload_encode(channel->founder_key); tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE, 4, csidp->data, csidp->len, mode, sizeof(mode), clidp->data, clidp->len, - fkey, fkey_len); + fkey ? fkey->data : NULL, + fkey ? fkey->len : 0); len = tmp->len; *channel_users_modes = silc_buffer_realloc(*channel_users_modes, @@ -3808,9 +4588,8 @@ void silc_server_announce_get_channel_users(SilcServer server, silc_buffer_put(*channel_users_modes, tmp->data, tmp->len); silc_buffer_pull(*channel_users_modes, len); silc_buffer_free(tmp); - silc_free(fkey); + silc_buffer_free(fkey); fkey = NULL; - fkey_len = 0; silc_buffer_free(clidp); } silc_hash_table_list_reset(&htl); @@ -3830,6 +4609,8 @@ void silc_server_announce_get_channels(SilcServer server, SilcBuffer **channel_users_modes, SilcUInt32 *channel_users_modes_c, SilcBuffer **channel_topics, + SilcBuffer **channel_invites, + SilcBuffer **channel_bans, SilcChannelID ***channel_ids, unsigned long creation_time) { @@ -3879,36 +4660,56 @@ void silc_server_announce_get_channels(SilcServer server, silc_buffer_pull(*channels, len); } - /* Channel user modes */ - *channel_users_modes = silc_realloc(*channel_users_modes, - 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; - - /* Channel's topic */ - *channel_topics = silc_realloc(*channel_topics, - sizeof(**channel_topics) * (i + 1)); - (*channel_topics)[i] = NULL; - silc_server_announce_get_channel_topic(server, channel, - &(*channel_topics)[i]); - i++; + if (creation_time && channel->updated < creation_time) + announce = FALSE; + else + announce = TRUE; + + if (announce) { + /* Channel user modes */ + *channel_users_modes = silc_realloc(*channel_users_modes, + 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; + + /* Channel's topic */ + *channel_topics = silc_realloc(*channel_topics, + sizeof(**channel_topics) * (i + 1)); + (*channel_topics)[i] = NULL; + silc_server_announce_get_channel_topic(server, channel, + &(*channel_topics)[i]); + + /* Channel's invite and ban list */ + *channel_invites = silc_realloc(*channel_invites, + sizeof(**channel_invites) * (i + 1)); + (*channel_invites)[i] = NULL; + *channel_bans = silc_realloc(*channel_bans, + sizeof(**channel_bans) * (i + 1)); + (*channel_bans)[i] = NULL; + silc_server_announce_get_inviteban(server, channel, + &(*channel_invites)[i], + &(*channel_bans)[i]); + + (*channel_users_modes_c)++; + silc_free(cid); + + i++; + } if (!silc_idcache_list_next(list, &id_cache)) break; } - - *channel_users_modes_c += i; } silc_idcache_list_free(list); @@ -3929,6 +4730,8 @@ void silc_server_announce_channels(SilcServer server, SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL; SilcBuffer *channel_users_modes = NULL; SilcBuffer *channel_topics = NULL; + SilcBuffer *channel_invites = NULL; + SilcBuffer *channel_bans = NULL; SilcUInt32 channel_users_modes_c = 0; SilcChannelID **channel_ids = NULL; @@ -3941,6 +4744,8 @@ void silc_server_announce_channels(SilcServer server, &channel_users_modes, &channel_users_modes_c, &channel_topics, + &channel_invites, + &channel_bans, &channel_ids, creation_time); /* Get channels and channel users in global list */ @@ -3951,6 +4756,8 @@ void silc_server_announce_channels(SilcServer server, &channel_users_modes, &channel_users_modes_c, &channel_topics, + &channel_invites, + &channel_bans, &channel_ids, creation_time); if (channels) { @@ -3966,6 +4773,20 @@ void silc_server_announce_channels(SilcServer server, silc_buffer_free(channels); } + if (channel_users) { + silc_buffer_push(channel_users, channel_users->data - channel_users->head); + SILC_LOG_HEXDUMP(("channel users"), channel_users->data, + channel_users->len); + + /* Send the packet */ + silc_server_packet_send(server, remote, + SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, + channel_users->data, channel_users->len, + FALSE); + + silc_buffer_free(channel_users); + } + if (channel_modes) { int i; @@ -3988,20 +4809,6 @@ void silc_server_announce_channels(SilcServer server, 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, - channel_users->len); - - /* Send the packet */ - silc_server_packet_send(server, remote, - SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, - channel_users->data, channel_users->len, - FALSE); - - silc_buffer_free(channel_users); - } - if (channel_users_modes) { int i; @@ -4047,26 +4854,53 @@ void silc_server_announce_channels(SilcServer server, silc_free(channel_topics); } - silc_free(channel_ids); -} + if (channel_invites) { + int i; -/* Failure timeout callback. If this is called then we will immediately - process the received failure. We always process the failure with timeout - since we do not want to blindly trust to received failure packets. - This won't be called (the timeout is cancelled) if the failure was - bogus (it is bogus if remote does not close the connection after sending - the failure). */ + for (i = 0; i < channel_users_modes_c; i++) { + if (!channel_invites[i]) + continue; -SILC_TASK_CALLBACK(silc_server_failure_callback) -{ - SilcServerFailureContext f = (SilcServerFailureContext)context; + silc_buffer_push(channel_invites[i], + channel_invites[i]->data - + channel_invites[i]->head); + SILC_LOG_HEXDUMP(("channel invite list"), channel_invites[i]->data, + channel_invites[i]->len); + silc_server_packet_send_dest(server, remote, + SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, + channel_ids[i], SILC_ID_CHANNEL, + channel_invites[i]->data, + channel_invites[i]->len, + FALSE); + silc_buffer_free(channel_invites[i]); + } + silc_free(channel_invites); + } + + if (channel_bans) { + int i; + + for (i = 0; i < channel_users_modes_c; i++) { + if (!channel_bans[i]) + continue; - if (f->sock->protocol) { - f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE; - silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0); + silc_buffer_push(channel_bans[i], + channel_bans[i]->data - + channel_bans[i]->head); + SILC_LOG_HEXDUMP(("channel ban list"), channel_bans[i]->data, + channel_bans[i]->len); + silc_server_packet_send_dest(server, remote, + SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST, + channel_ids[i], SILC_ID_CHANNEL, + channel_bans[i]->data, + channel_bans[i]->len, + FALSE); + silc_buffer_free(channel_bans[i]); + } + silc_free(channel_bans); } - silc_free(f); + silc_free(channel_ids); } /* Assembles user list and users mode list from the `channel'. */ @@ -4141,9 +4975,9 @@ void silc_server_save_users_on_channel(SilcServer server, SilcClientEntry client; SilcIDCacheEntry cache; SilcChannelClientEntry chl; - bool global; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Saving %d users on %s channel", user_count, + channel->channel_name)); for (i = 0; i < user_count; i++) { /* Client ID */ @@ -4163,21 +4997,19 @@ void silc_server_save_users_on_channel(SilcServer server, continue; } - global = FALSE; + cache = NULL; /* Check if we have this client cached already. */ client = silc_idlist_find_client_by_id(server->local_list, client_id, server->server_type, &cache); - if (!client) { + if (!client) client = silc_idlist_find_client_by_id(server->global_list, client_id, server->server_type, &cache); - global = TRUE; - } if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) { + if (server->server_type != SILC_SERVER) { silc_free(client_id); continue; } @@ -4195,15 +5027,18 @@ void silc_server_save_users_on_channel(SilcServer server, } client->data.status |= SILC_IDLIST_STATUS_REGISTERED; - } else { - /* Found, if it is from global list we'll assure that we won't - expire it now that the entry is on channel. */ - if (global) - cache->expire = 0; } + if (cache) + cache->expire = 0; silc_free(client_id); + if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { + SILC_LOG_ERROR(("Attempting to add unregistered client to channel ", + "%s", channel->channel_name)); + continue; + } + if (!silc_server_client_on_channel(client, channel, &chl)) { /* Client was not on the channel, add it. */ chl = silc_calloc(1, sizeof(*chl)); @@ -4241,20 +5076,21 @@ void silc_server_save_user_channels(SilcServer server, char *name; int i = 0; - if (!channels ||!channels_user_modes) + if (!channels || !channels_user_modes || + !(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) 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, + 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 = silc_idlist_find_channel_by_id(server->local_list, channel_id, NULL); if (!channel) channel = silc_idlist_find_channel_by_id(server->global_list, @@ -4265,7 +5101,7 @@ void silc_server_save_user_channels(SilcServer server, 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, @@ -4390,7 +5226,7 @@ silc_server_get_client_route(SilcServer server, silc_free(id); if (idata) *idata = (SilcIDListData)server->router; - return server->router->connection; + return SILC_PRIMARY_ROUTE(server); } /* We are router and we will perform route lookup for the destination @@ -4486,79 +5322,88 @@ SilcBuffer silc_server_get_client_channel_list(SilcServer server, return buffer; } -/* Finds client entry by Client ID and if it is not found then resolves - it using WHOIS command. */ +/* Timeout callback for unsuccessful rekey. The rekey did not go through + for some reason. */ -SilcClientEntry silc_server_get_client_resolve(SilcServer server, - SilcClientID *client_id, - bool always_resolve, - bool *resolved) +SILC_TASK_CALLBACK(silc_server_rekey_timeout) { - SilcClientEntry client; - - if (resolved) - *resolved = FALSE; - - client = silc_idlist_find_client_by_id(server->local_list, client_id, - TRUE, NULL); - if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, - client_id, TRUE, NULL); - if (!client && server->server_type == SILC_ROUTER) - return NULL; - } - - if (!client && server->standalone) - return NULL; - - if (!client || !client->nickname || !client->username || - always_resolve) { - SilcBuffer buffer, idp; - - if (client) { - client->data.status |= SILC_IDLIST_STATUS_RESOLVING; - client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED; - client->resolve_cmd_ident = ++server->cmd_ident; - } + SilcServerRekeyInternalContext *ctx = context; + SilcServer server = app_context; + SilcSocketConnection sock = ctx->sock; - idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT); - buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS, - server->cmd_ident, 1, - 4, idp->data, idp->len); - silc_server_packet_send(server, client ? client->router->connection : - server->router->connection, - SILC_PACKET_COMMAND, 0, - buffer->data, buffer->len, FALSE); - silc_buffer_free(idp); - silc_buffer_free(buffer); + SILC_LOG_DEBUG(("Timeout occurred in rekey protocol with %s:%d [%s]", + sock->hostname, sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); - if (resolved) - *resolved = TRUE; + SILC_LOG_WARNING(("Timeout occurred in rekey protocol with %s:%d [%s]", + sock->hostname, sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); - return NULL; + if (sock->protocol) { + silc_protocol_cancel(sock->protocol, server->schedule); + silc_protocol_free(sock->protocol); + sock->protocol = NULL; } - - return client; + if (ctx->packet) + silc_packet_context_free(ctx->packet); + if (ctx->ske) + silc_ske_free(ctx->ske); + silc_socket_free(sock); + silc_free(ctx); } /* A timeout callback for the re-key. We will be the initiator of the re-key protocol. */ -SILC_TASK_CALLBACK(silc_server_rekey_callback) +SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_callback) { + SilcServer server = app_context; SilcSocketConnection sock = (SilcSocketConnection)context; SilcIDListData idata = (SilcIDListData)sock->user_data; - SilcServer server = (SilcServer)idata->rekey->context; SilcProtocol protocol; SilcServerRekeyInternalContext *proto_ctx; - SILC_LOG_DEBUG(("Start")); + if (!idata) + return; + + /* Do not execute rekey with disabled connections, as it would not + go through anyway. */ + if (idata->status & SILC_IDLIST_STATUS_DISABLED) + return; + + /* If rekey protocol is active already wait for it to finish */ + if (sock->protocol && sock->protocol->protocol && + sock->protocol->protocol->type == SILC_PROTOCOL_SERVER_REKEY) + return; + + /* If any other protocol is active do not start this protocol yet. */ + if (sock->protocol) { + SILC_LOG_DEBUG(("Waiting for other protocol to finish before rekeying")); + silc_schedule_task_add(server->schedule, sock->sock, + silc_server_rekey_callback, + sock, 60, 0, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); + return; + } + + SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s]", + sock->hostname, sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); /* 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->sock = sock; + proto_ctx->sock = silc_socket_dup(sock); proto_ctx->responder = FALSE; proto_ctx->pfs = idata->rekey->pfs; @@ -4568,14 +5413,17 @@ SILC_TASK_CALLBACK(silc_server_rekey_callback) &protocol, proto_ctx, silc_server_rekey_final); sock->protocol = protocol; + /* Register timeout callback in case the rekey does not go through. */ + proto_ctx->timeout_task = + silc_schedule_task_add(server->schedule, sock->sock, + silc_server_rekey_timeout, + proto_ctx, + server->config->key_exchange_timeout, 0, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + /* Run the protocol */ silc_protocol_execute(protocol, server->schedule, 0, 0); - - /* Re-register re-key timeout */ - silc_schedule_task_add(server->schedule, sock->sock, - silc_server_rekey_callback, - context, idata->rekey->timeout, 0, - SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); } /* The final callback for the REKEY protocol. This will actually take the @@ -4588,13 +5436,16 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final) (SilcServerRekeyInternalContext *)protocol->context; SilcServer server = (SilcServer)ctx->server; SilcSocketConnection sock = ctx->sock; + SilcIDListData idata = (SilcIDListData)sock->user_data; - SILC_LOG_DEBUG(("Start")); + if (ctx->timeout_task) + silc_schedule_task_del(server->schedule, ctx->timeout_task); if (protocol->state == SILC_PROTOCOL_STATE_ERROR || protocol->state == SILC_PROTOCOL_STATE_FAILURE) { /* Error occured during protocol */ - SILC_LOG_ERROR(("Error occurred during rekey protocol")); + SILC_LOG_ERROR(("Error occurred during rekey protocol with " + "%s (%s)", sock->hostname, sock->ip)); silc_protocol_cancel(protocol, server->schedule); silc_protocol_free(protocol); sock->protocol = NULL; @@ -4602,14 +5453,35 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); + silc_socket_free(sock); silc_free(ctx); + silc_server_disconnect_remote(server, sock, + SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL); + + /* Reconnect */ + if (sock->type != SILC_SOCKET_TYPE_CLIENT) + silc_server_create_connections(server); return; } + SILC_LOG_DEBUG(("Rekey protocol completed with %s:%d [%s]", + sock->hostname, sock->port, + (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" : + sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" : + sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" : + "Router"))); + /* Purge the outgoing data queue to assure that all rekey packets really go to the network before we quit the protocol. */ silc_server_packet_queue_purge(server, sock); + /* Re-register re-key timeout */ + if (ctx->responder == FALSE) + silc_schedule_task_add(server->schedule, sock->sock, + silc_server_rekey_callback, + sock, idata->rekey->timeout, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + /* Cleanup */ silc_protocol_free(protocol); sock->protocol = NULL; @@ -4617,6 +5489,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); + silc_socket_free(sock); silc_free(ctx); } @@ -4628,14 +5501,13 @@ SILC_TASK_CALLBACK(silc_server_get_stats) SilcServer server = (SilcServer)context; SilcBuffer idp, packet; - SILC_LOG_DEBUG(("Retrieving stats from router")); - if (!server->standalone) { + SILC_LOG_DEBUG(("Retrieving stats from router")); idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER); - packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, + 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_server_packet_send(server, SILC_PRIMARY_ROUTE(server), SILC_PACKET_COMMAND, 0, packet->data, packet->len, FALSE); silc_buffer_free(packet);