X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fserver.c;h=963746f9045acc439a214a8294b17724b529f0cd;hp=10c72d22b511a8148d188dd76fe7aecce0b385b6;hb=413da0f8686910f5e627393157566ae729ca99c4;hpb=dc44e6c61393a3f69a02e7e5c22df5e0506eb8bc diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 10c72d22..963746f9 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -1,10 +1,10 @@ /* - server.c + server.c 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 @@ -30,7 +30,6 @@ 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 @@ -86,9 +83,10 @@ void silc_server_free(SilcServer server) #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); @@ -201,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; } @@ -216,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, @@ -252,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; @@ -285,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; @@ -320,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; @@ -342,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); @@ -427,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 @@ -448,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 @@ -474,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); @@ -561,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; @@ -574,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, @@ -618,14 +614,15 @@ bool silc_server_rehash(SilcServer server) /* Check whether new config has this one too */ for (newptr = newconfig->routers; newptr; newptr = newptr->next) { - if (!strcmp(newptr->host, ptr->host) && newptr->port == ptr->port && + if (silc_string_compare(newptr->host, ptr->host) && + newptr->port == ptr->port && newptr->initiator == ptr->initiator) { found = TRUE; break; } } - if (!found) { + if (!found && ptr->host) { /* Remove this connection */ SilcSocketConnection sock; sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER, @@ -649,13 +646,13 @@ bool silc_server_rehash(SilcServer server) /* Check whether new config has this one too */ for (newptr = newconfig->servers; newptr; newptr = newptr->next) { - if (!strcmp(newptr->host, ptr->host)) { + if (silc_string_compare(newptr->host, ptr->host)) { found = TRUE; break; } } - if (!found) { + if (!found && ptr->host) { /* Remove this connection */ SilcSocketConnection sock; sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_SERVER, @@ -669,12 +666,38 @@ bool silc_server_rehash(SilcServer server) } } - /* 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); + 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) { @@ -702,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; @@ -746,13 +779,17 @@ void silc_server_stop(SilcServer server) silc_schedule_task_del_by_context(server->schedule, server->sockets[i]); - silc_server_disconnect_remote(server, server->sockets[i], - SILC_STATUS_OK, + 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 (sock->user_data) - silc_server_free_sock_user_data(server, sock, - "Server is shutting down"); - silc_socket_free(sock); + 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; @@ -860,8 +897,8 @@ 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); @@ -916,8 +953,8 @@ 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; @@ -960,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; } @@ -974,7 +1015,7 @@ 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; @@ -1017,20 +1058,53 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router) 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; @@ -1078,9 +1152,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) silc_ske_free(ctx->ske); silc_free(ctx->dest_id); 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); /* Try reconnecting if configuration wants it */ @@ -1123,9 +1195,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) silc_ske_free(ctx->ske); silc_free(ctx->dest_id); 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); /* Try reconnecting if configuration wants it */ @@ -1199,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; } @@ -1248,7 +1316,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) SilcSocketConnection sock = ctx->sock; SilcServerEntry id_entry = NULL; SilcBuffer packet; - SilcServerHBContext hb_context; unsigned char *id_string; SilcUInt32 id_len; SilcIDListData idata; @@ -1261,8 +1328,10 @@ 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) { @@ -1332,8 +1401,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) 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; } @@ -1350,17 +1421,13 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) 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, @@ -1372,26 +1439,37 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) 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. 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 data if we are not backup router (unless not as primary + currently). Backup router announces later at the end of + resuming protocol. */ + if (server->backup_router && server->server_type == SILC_ROUTER) { + SILC_LOG_DEBUG(("Announce data after resume protocol")); + } else { + /* If we are router then announce our possible servers. Backup + router announces also global servers. */ + if (server->server_type == SILC_ROUTER) + silc_server_announce_servers(server, + server->backup_router ? TRUE : FALSE, + 0, SILC_PRIMARY_ROUTE(server)); - /* Announce our clients and channels to the router */ - silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server)); - silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server)); + /* Announce our clients and channels to the router */ + silc_server_announce_clients(server, 0, SILC_PRIMARY_ROUTE(server)); + silc_server_announce_channels(server, 0, SILC_PRIMARY_ROUTE(server)); + } /* If we are backup router then this primary router is whom we are backing up. */ if (server->server_type == SILC_BACKUP_ROUTER) - silc_server_backup_add(server, server->id_entry, sock->ip, 0, TRUE); + 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); } @@ -1446,8 +1524,6 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock, SilcServerConfigDeny *deny; int port; - context = (void *)server; - /* Check whether we could resolve both IP and FQDN. */ if (!sock->ip || (!strcmp(sock->ip, sock->hostname) && server->config->require_reverse_lookup)) { @@ -1468,6 +1544,7 @@ 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, @@ -1634,7 +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 key exchange protocol")); + SILC_LOG_DEBUG(("Error in key exchange protocol")); silc_protocol_free(protocol); sock->protocol = NULL; silc_ske_free_key_material(ctx->keymat); @@ -1647,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; } @@ -1680,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; @@ -1729,7 +1812,7 @@ 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. +/* After this is called, server don't wait for backup router anymore. This gets called automatically even after we have backup router connection established. */ @@ -1750,10 +1833,9 @@ 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; + SilcServerConfigConnParams *param = &server->config->param; if (protocol->state == SILC_PROTOCOL_STATE_ERROR || protocol->state == SILC_PROTOCOL_STATE_FAILURE) { @@ -1770,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; } @@ -1808,7 +1897,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) router->backup_replace_ip, 0)) { SILC_LOG_INFO(("Will not accept connections because we do " "not have backup router connection established")); - silc_server_disconnect_remote(server, sock, + 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"); @@ -1836,7 +1926,8 @@ 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; @@ -1850,11 +1941,21 @@ 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 (conn->param->anonymous) + if (param->anonymous) client->mode |= SILC_UMODE_ANONYMOUS; } @@ -1881,7 +1982,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) !SILC_PRIMARY_ROUTE(server)) { SILC_LOG_INFO(("Will not accept server connection because we do " "not have primary router connection established")); - silc_server_disconnect_remote(server, sock, + 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"); @@ -1903,8 +2005,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) 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; @@ -1927,8 +2039,18 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) } 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; @@ -1949,7 +2071,8 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) router->backup_replace_ip, 0)) { SILC_LOG_INFO(("Will not accept connections because we do " "not have backup router connection established")); - silc_server_disconnect_remote(server, sock, + 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"); @@ -1994,7 +2117,8 @@ 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; @@ -2011,6 +2135,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) 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, @@ -2061,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) @@ -2096,6 +2227,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process) SilcCipher cipher = NULL; SilcHmac hmac = NULL; SilcUInt32 sequence = 0; + bool local_is_router; int ret; if (!sock) { @@ -2107,7 +2239,7 @@ SILC_TASK_CALLBACK(silc_server_packet_process) 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; } @@ -2122,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, @@ -2129,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; } @@ -2149,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; } @@ -2173,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; } @@ -2208,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); } } @@ -2249,7 +2406,7 @@ 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 (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")); @@ -2328,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; @@ -2336,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; } @@ -2372,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: @@ -2413,16 +2585,29 @@ void silc_server_packet_parse_type(SilcServer server, message = silc_memdup(packet->buffer->data + 1, packet->buffer->len - 1); - SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", + 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; @@ -2446,16 +2631,46 @@ void silc_server_packet_parse_type(SilcServer server, */ 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; @@ -2568,7 +2783,12 @@ 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; @@ -2611,7 +2831,12 @@ 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; @@ -2654,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; @@ -2691,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; @@ -2790,7 +3025,12 @@ 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; @@ -2831,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; @@ -2844,7 +3083,20 @@ void silc_server_create_connection(SilcServer server, SILC_TASK_CALLBACK(silc_server_close_connection_final) { - silc_socket_free(context); + SilcServer server = app_context; + SilcSocketConnection sock = context; + + SILC_LOG_DEBUG(("Deleting socket %p", sock)); + + /* 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); + + silc_socket_free(sock); } /* Closes connection to socket connection */ @@ -2854,57 +3106,49 @@ void silc_server_close_connection(SilcServer server, { char tmp[128]; - if (!server->sockets[sock->sock] && SILC_IS_DISCONNECTED(sock)) { - silc_schedule_task_add(server->schedule, 0, + 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 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; } 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 : "")); - - /* We won't listen for this connection anymore */ - silc_schedule_unset_listen_fd(server->schedule, sock->sock); - - /* Unregister all tasks */ - silc_schedule_task_del_by_fd(server->schedule, sock->sock); - - /* Close the actual connection */ - silc_net_close_connection(sock->sock); - server->sockets[sock->sock] = NULL; - - /* 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_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; - } - } + 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_schedule_task_add(server->schedule, 0, + 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, @@ -2919,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 *); @@ -2953,24 +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(i->client->channels)); + 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. */ @@ -2983,16 +3228,6 @@ void silc_server_free_client_data(SilcServer server, { SILC_LOG_DEBUG(("Freeing client data")); -#if 1 - if (!client->router && !client->connection && - !(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { - SILC_LOG_ERROR(("****** freeing data for already unregistered client -s")); - SILC_LOG_ERROR(("****** Contact Pekka")); - SILC_LOG_ERROR(("****** freeing data for already unregistered client -e")); - return; - } -#endif - /* If there is pending outgoing data for the client then purge it to the network before removing the client entry. */ silc_server_packet_queue_purge(server, sock); @@ -3013,10 +3248,10 @@ void silc_server_free_client_data(SilcServer server, /* Remove client from all channels */ if (notify) silc_server_remove_from_channels(server, NULL, client, - TRUE, (char *)signoff, TRUE); + TRUE, (char *)signoff, TRUE, FALSE); else silc_server_remove_from_channels(server, NULL, client, - FALSE, NULL, FALSE); + FALSE, NULL, FALSE, FALSE); /* Remove this client from watcher list if it is */ silc_server_del_from_watcher_list(server, client); @@ -3034,12 +3269,9 @@ void silc_server_free_client_data(SilcServer server, into history (for WHOWAS command) for 5 minutes, unless we're shutting down server. */ if (!server->server_shutdown) { - FreeClientInternal i = silc_calloc(1, sizeof(*i)); - i->server = server; - i->client = client; silc_schedule_task_add(server->schedule, 0, silc_server_free_client_data_timeout, - (void *)i, 300, 0, + client, 300, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW); client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; client->data.status &= ~SILC_IDLIST_STATUS_LOCAL; @@ -3061,6 +3293,17 @@ void silc_server_free_sock_user_data(SilcServer server, SilcSocketConnection sock, const char *signoff_message) { + + /* 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: { @@ -3085,7 +3328,7 @@ void silc_server_free_sock_user_data(SilcServer server, sock->type != SILC_SOCKET_TYPE_ROUTER) backup_router = NULL; - if (server->server_shutdown) + if (server->server_shutdown || server->backup_noswitch) backup_router = NULL; /* If this was our primary router connection then we're lost to @@ -3093,15 +3336,12 @@ void silc_server_free_sock_user_data(SilcServer server, 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 { if (server->id_entry != backup_router) { @@ -3111,17 +3351,23 @@ void silc_server_free_sock_user_data(SilcServer server, server->router = backup_router; server->router_connect = time(0); server->backup_primary = TRUE; + 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; - - /* We stop here to take a breath */ - sleep(2); } - 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 @@ -3138,23 +3384,39 @@ 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) { /* 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. */ - silc_server_remove_clients_by_server(server, user_data, - user_data, TRUE); + 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) @@ -3203,6 +3465,7 @@ void silc_server_free_sock_user_data(SilcServer server, server->server_name, server->router->server_name)); } + server->backup_noswitch = FALSE; /* Free the server entry */ silc_server_backup_del(server, user_data); @@ -3247,15 +3510,6 @@ void silc_server_free_sock_user_data(SilcServer server, } } - /* If any protocol is active cancel its execution */ - if (sock->protocol) { - 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; - } - sock->user_data = NULL; } @@ -3268,7 +3522,8 @@ 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; @@ -3278,11 +3533,12 @@ void silc_server_remove_from_channels(SilcServer server, if (!client) return; - SILC_LOG_DEBUG(("Removing client from joined channels")); - 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) @@ -3304,7 +3560,7 @@ void silc_server_remove_from_channels(SilcServer server, } 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 @@ -3313,6 +3569,7 @@ void silc_server_remove_from_channels(SilcServer server, chl->client->router && !silc_server_channel_has_global(channel)) channel->global_users = FALSE; + memset(chl, 'A', sizeof(*chl)); silc_free(chl); /* Update statistics */ @@ -3330,7 +3587,7 @@ void silc_server_remove_from_channels(SilcServer 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, @@ -3344,13 +3601,28 @@ 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; @@ -3389,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 @@ -3405,8 +3677,8 @@ bool silc_server_remove_from_one_channel(SilcServer server, 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 @@ -3415,6 +3687,7 @@ bool silc_server_remove_from_one_channel(SilcServer server, chl->client->router && !silc_server_channel_has_global(channel)) channel->global_users = FALSE; + memset(chl, 'O', sizeof(*chl)); silc_free(chl); /* Update statistics */ @@ -3436,7 +3709,7 @@ bool silc_server_remove_from_one_channel(SilcServer 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); @@ -3448,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); @@ -3476,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; @@ -3485,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 @@ -3686,8 +3960,8 @@ 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; @@ -3752,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))); @@ -3761,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) @@ -3817,8 +4095,9 @@ 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; } } @@ -3863,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))); @@ -3874,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); @@ -3903,13 +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:%d (%s)", sock->hostname, + 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 @@ -4147,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, @@ -4158,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, @@ -4195,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); @@ -4224,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, @@ -4242,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); @@ -4264,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) { @@ -4331,7 +4678,7 @@ void silc_server_announce_get_channels(SilcServer server, sizeof(**channel_ids) * (i + 1)); (*channel_ids)[i] = NULL; silc_server_announce_get_channel_users(server, channel, - &(*channel_modes)[i], + &(*channel_modes)[i], channel_users, &(*channel_users_modes)[i]); (*channel_ids)[i] = channel->id; @@ -4342,7 +4689,21 @@ void silc_server_announce_get_channels(SilcServer server, (*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++; } @@ -4369,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; @@ -4381,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 */ @@ -4391,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) { @@ -4487,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 (f->sock->protocol) { - f->sock->protocol->state = SILC_PROTOCOL_STATE_FAILURE; - silc_protocol_execute(f->sock->protocol, f->server->schedule, 0, 0); + if (channel_bans) { + int i; + + for (i = 0; i < channel_users_modes_c; i++) { + if (!channel_bans[i]) + continue; + + 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'. */ @@ -4685,18 +5079,18 @@ void silc_server_save_user_channels(SilcServer server, 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, @@ -4707,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, @@ -4928,77 +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 : - SILC_PRIMARY_ROUTE(server), - 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; + 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; @@ -5008,16 +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); - - SILC_LOG_DEBUG(("Rekey protocol completed")); - - /* 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 @@ -5030,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; @@ -5044,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; @@ -5059,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); } @@ -5070,11 +5501,10 @@ 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, SILC_PRIMARY_ROUTE(server),