X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fserver.c;h=2e85203df1d7335de7d244086c677cfb91a385b4;hp=f3022c2dd544006ecd398a6a42f01c465e408e38;hb=e5d8d3db6caa344b3d419b884556c21b15e7d123;hpb=2ccba0fda23268cb45841b5984fc31b4287a3d4b diff --git a/apps/silcd/server.c b/apps/silcd/server.c index f3022c2d..2e85203d 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -77,10 +77,8 @@ void silc_server_free(SilcServer server) SilcSimContext *sim; #endif - if (server->local_list) - silc_free(server->local_list); - if (server->global_list) - silc_free(server->global_list); + silc_free(server->local_list); + silc_free(server->global_list); if (server->rng) silc_rng_free(server->rng); @@ -95,8 +93,7 @@ void silc_server_free(SilcServer server) silc_dlist_uninit(server->sim); #endif - if (server->params) - silc_free(server->params); + silc_free(server->params); if (server->pending_commands) silc_dlist_uninit(server->pending_commands); @@ -307,8 +304,19 @@ int silc_server_init(SilcServer server) /* If server connections has been configured then we must be router as normal server cannot have server connections, only router connections. */ - if (server->config->servers) + if (server->config->servers) { + SilcServerConfigSectionServerConnection *ptr = server->config->servers; + server->server_type = SILC_ROUTER; + while (ptr) { + if (ptr->backup_router) { + server->server_type = SILC_BACKUP_ROUTER; + server->backup_router = TRUE; + break; + } + ptr = ptr->next; + } + } /* Register the ID Cache purge task. This periodically purges the ID cache and removes the expired cache entries. */ @@ -447,6 +455,21 @@ void silc_server_daemonise(SilcServer server) } } +/* The heart of the server. This runs the scheduler thus runs the server. + When this returns the server has been stopped and the program will + be terminated. */ + +void silc_server_run(SilcServer server) +{ + SILC_LOG_DEBUG(("Running server")); + + SILC_LOG_INFO(("SILC Server started")); + + /* Start the scheduler, the heart of the SILC server. When this returns + the program will be terminated. */ + silc_schedule(server->schedule); +} + /* Stops the SILC server. This function is used to shutdown the server. This is usually called after the scheduler has returned. After stopping the server one should call silc_server_free. */ @@ -463,19 +486,71 @@ void silc_server_stop(SilcServer server) SILC_LOG_DEBUG(("Server stopped")); } -/* The heart of the server. This runs the scheduler thus runs the server. - When this returns the server has been stopped and the program will - be terminated. */ +/* Function that is called when the network connection to a router has + been established. This will continue with the key exchange protocol + with the remote router. */ -void silc_server_run(SilcServer server) +void silc_server_start_key_exchange(SilcServer server, + SilcServerConnection sconn, + int sock) { - SILC_LOG_DEBUG(("Running server")); + SilcSocketConnection newsocket; + SilcProtocol protocol; + SilcServerKEInternalContext *proto_ctx; + void *context; - SILC_LOG_INFO(("SILC Server started")); + /* Set socket options */ + silc_net_set_socket_nonblock(sock); + silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); - /* Start the scheduler, the heart of the SILC server. When this returns - the program will be terminated. */ - silc_schedule(server->schedule); + /* Create socket connection for the connection. Even though we + know that we are connecting to a router we will mark the socket + to be unknown connection until we have executed authentication + protocol. */ + silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); + server->sockets[sock] = newsocket; + newsocket->hostname = strdup(sconn->remote_host); + newsocket->ip = strdup(sconn->remote_host); + newsocket->port = sconn->remote_port; + sconn->sock = newsocket; + + /* Allocate internal protocol context. This is sent as context + to the protocol. */ + proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); + proto_ctx->server = (void *)server; + proto_ctx->context = (void *)sconn; + proto_ctx->sock = newsocket; + proto_ctx->rng = server->rng; + proto_ctx->responder = FALSE; + + /* Perform key exchange protocol. silc_server_connect_to_router_second + will be called after the protocol is finished. */ + silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, + &protocol, proto_ctx, + silc_server_connect_to_router_second); + newsocket->protocol = protocol; + + /* Register a timeout task that will be executed if the protocol + is not executed within set limit. */ + proto_ctx->timeout_task = + silc_schedule_task_add(server->schedule, sock, + silc_server_timeout_remote, + server, server->params->protocol_timeout, + server->params->protocol_timeout_usec, + SILC_TASK_TIMEOUT, + SILC_TASK_PRI_LOW); + + /* Register the connection for network input and output. This sets + that scheduler will listen for incoming packets for this connection + and sets that outgoing packets may be sent to this connection as + well. However, this doesn't set the scheduler for outgoing traffic, + it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, + later when outgoing data is available. */ + context = (void *)server; + SILC_REGISTER_CONNECTION_FOR_IO(sock); + + /* Run the protocol */ + silc_protocol_execute(protocol, server->schedule, 0, 0); } /* Timeout callback that will be called to retry connecting to remote @@ -522,14 +597,14 @@ SILC_TASK_CALLBACK(silc_server_connect_router) { SilcServerConnection sconn = (SilcServerConnection)context; SilcServer server = sconn->server; - SilcSocketConnection newsocket; - SilcProtocol protocol; - SilcServerKEInternalContext *proto_ctx; int sock; - SILC_LOG_INFO(("Connecting to the router %s on port %d", + SILC_LOG_INFO(("Connecting to the %s %s on port %d", + (sconn->backup ? "backup router" : "router"), sconn->remote_host, sconn->remote_port)); + server->router_connect = time(0); + /* Connect to remote host */ sock = silc_net_create_connection(server->config->listen_port->local_ip, sconn->remote_port, @@ -543,58 +618,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router) return; } - /* Set socket options */ - silc_net_set_socket_nonblock(sock); - silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); - - /* Create socket connection for the connection. Even though we - know that we are connecting to a router we will mark the socket - to be unknown connection until we have executed authentication - protocol. */ - silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket); - server->sockets[sock] = newsocket; - newsocket->hostname = strdup(sconn->remote_host); - newsocket->ip = strdup(sconn->remote_host); - newsocket->port = sconn->remote_port; - sconn->sock = newsocket; - - /* Allocate internal protocol context. This is sent as context - to the protocol. */ - proto_ctx = silc_calloc(1, sizeof(*proto_ctx)); - proto_ctx->server = (void *)server; - proto_ctx->context = (void *)sconn; - proto_ctx->sock = newsocket; - proto_ctx->rng = server->rng; - proto_ctx->responder = FALSE; - - /* Perform key exchange protocol. silc_server_connect_to_router_second - will be called after the protocol is finished. */ - silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, - &protocol, proto_ctx, - silc_server_connect_to_router_second); - newsocket->protocol = protocol; - - /* Register a timeout task that will be executed if the protocol - is not executed within set limit. */ - proto_ctx->timeout_task = - silc_schedule_task_add(server->schedule, sock, - silc_server_timeout_remote, - server, server->params->protocol_timeout, - server->params->protocol_timeout_usec, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_LOW); - - /* Register the connection for network input and output. This sets - that scheduler will listen for incoming packets for this connection - and sets that outgoing packets may be sent to this connection as - well. However, this doesn't set the scheduler for outgoing traffic, - it will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT, - later when outgoing data is available. */ - context = (void *)server; - SILC_REGISTER_CONNECTION_FOR_IO(sock); - - /* Run the protocol */ - silc_protocol_execute(protocol, server->schedule, 0, 0); + /* Continue with key exchange protocol */ + silc_server_start_key_exchange(server, sconn, sock); } /* This function connects to our primary router or if we are a router this @@ -606,64 +631,45 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router) { SilcServer server = (SilcServer)context; SilcServerConnection sconn; + SilcServerConfigSectionServerConnection *ptr; SILC_LOG_DEBUG(("Connecting to router(s)")); - /* If we are normal SILC server we need to connect to our cell's - router. */ 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")); + } - /* Create connection to the router, if configured. */ - if (server->config->routers) { + /* Create the connections to all our routes */ + ptr = server->config->routers; + while (ptr) { + + SILC_LOG_DEBUG(("%s connection [%s] %s:%d", + ptr->backup_router ? "Backup router" : "Router", + ptr->initiator ? "Initiator" : "Responder", + ptr->host, ptr->port)); + if (ptr->initiator) { /* Allocate connection object for hold connection specific stuff. */ sconn = silc_calloc(1, sizeof(*sconn)); sconn->server = server; - sconn->remote_host = strdup(server->config->routers->host); - sconn->remote_port = server->config->routers->port; - + sconn->remote_host = strdup(ptr->host); + sconn->remote_port = ptr->port; + sconn->backup = ptr->backup_router; + silc_schedule_task_add(server->schedule, fd, - silc_server_connect_router, - (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); - return; + silc_server_connect_router, + (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); } - } - - /* If we are a SILC router we need to establish all of our primary - routes. */ - if (server->server_type == SILC_ROUTER) { - SilcServerConfigSectionServerConnection *ptr; - SILC_LOG_DEBUG(("We are router")); - - /* Create the connections to all our routes */ - ptr = server->config->routers; - while (ptr) { - - SILC_LOG_DEBUG(("Router connection [%s] %s:%d", - ptr->initiator ? "Initiator" : "Responder", - ptr->host, ptr->port)); - - if (ptr->initiator) { - /* 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; - - silc_schedule_task_add(server->schedule, fd, - silc_server_connect_router, - (void *)sconn, 0, 1, SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); - } - - if (!ptr->next) - return; - - ptr = ptr->next; - } + if (!ptr->next) + return; + + ptr = ptr->next; } SILC_LOG_DEBUG(("No router(s), server will be standalone")); @@ -699,11 +705,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_free(ctx); silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Key exchange failed"); return; @@ -726,11 +731,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_free(ctx); silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Key exchange failed"); return; @@ -768,8 +772,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_free(ctx); silc_schedule_task_del_by_callback(server->schedule, silc_server_failure_callback); @@ -830,8 +833,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) if (protocol->state == SILC_PROTOCOL_STATE_ERROR || protocol->state == SILC_PROTOCOL_STATE_FAILURE) { /* Error occured during protocol */ - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Authentication failed"); goto out; @@ -839,7 +841,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) /* Add a task to the queue. This task receives new connections to the server. This task remains on the queue until the end of the program. */ - if (!server->listenning) { + if (!server->listenning && !sconn->backup) { silc_schedule_task_add(server->schedule, server->sock, silc_server_accept_new_connection, (void *)server, 0, 0, @@ -871,12 +873,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) SILC_LOG_INFO(("Connected to router %s", sock->hostname)); /* Add the connected router to local server list */ - server->standalone = FALSE; id_entry = silc_idlist_add_server(server->local_list, strdup(sock->hostname), SILC_ROUTER, ctx->dest_id, NULL, sock); if (!id_entry) { - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Authentication failed"); goto out; @@ -886,8 +886,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) silc_free(sock->user_data); sock->user_data = (void *)id_entry; sock->type = SILC_SOCKET_TYPE_ROUTER; - server->id_entry->router = id_entry; - server->router = id_entry; idata = (SilcIDListData)sock->user_data; idata->status |= SILC_IDLIST_STATUS_REGISTERED; @@ -904,17 +902,34 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final) idata->rekey->timeout = 3600; /* XXX hardcoded */ idata->rekey->context = (void *)server; silc_schedule_task_add(server->schedule, sock->sock, - silc_server_rekey_callback, - (void *)sock, idata->rekey->timeout, 0, - SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + silc_server_rekey_callback, + (void *)sock, idata->rekey->timeout, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); - /* If we are router then announce our possible servers. */ - if (server->server_type == SILC_ROUTER) - silc_server_announce_servers(server); + if (!sconn->backup) { + /* Mark this router our primary router if we're still standalone */ + if (server->standalone) { + server->id_entry->router = id_entry; + server->router = id_entry; + server->standalone = FALSE; + } + + /* If we are router then announce our possible servers. */ + if (server->server_type == SILC_ROUTER) + silc_server_announce_servers(server, FALSE, 0); + + /* Announce our clients and channels to the router */ + silc_server_announce_clients(server, 0); + silc_server_announce_channels(server, 0); + } else { + /* Add this server to be our backup router */ + silc_server_backup_add(server, id_entry, FALSE); + } - /* Announce our clients and channels to the router */ - silc_server_announce_clients(server); - silc_server_announce_channels(server); + /* Call the completion callback to indicate that we've connected to + the router */ + if (sconn->callback) + (*sconn->callback)(server, id_entry, sconn->callback_context); out: /* Free the temporary connection data context */ @@ -1124,11 +1139,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_free(ctx); silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Key exchange failed"); server->stat.auth_failures++; @@ -1152,11 +1166,10 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_second) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_free(ctx); silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Key exchange failed"); server->stat.auth_failures++; @@ -1229,13 +1242,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_free(ctx); if (sock) sock->protocol = NULL; silc_schedule_task_del_by_callback(server->schedule, - silc_server_failure_callback); + silc_server_failure_callback); silc_server_disconnect_remote(server, sock, "Server closed connection: " "Authentication failed"); server->stat.auth_failures++; @@ -1287,10 +1299,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) SILC_LOG_DEBUG(("Remote host is %s", ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? - "server" : "router")); + "server" : (conn->backup_router ? + "backup router" : "router"))); SILC_LOG_INFO(("Connection from %s (%s) is %s", sock->hostname, sock->ip, ctx->conn_type == SILC_SOCKET_TYPE_SERVER ? - "server" : "router")); + "server" : (conn->backup_router ? + "backup router" : "router"))); /* Add the server into server cache. The server name and Server ID is updated after we have received NEW_SERVER packet from the @@ -1321,8 +1335,28 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) 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 && conn->backup_router) { + silc_server_backup_add(server, new_server, conn->backup_local); + + /* Change it back to SERVER type since that's what it really is. */ + if (conn->backup_local) { + ctx->conn_type = SILC_SOCKET_TYPE_SERVER; + new_server->server_type = SILC_SOCKET_TYPE_SERVER; + } + } + +#if 0 + /* If the incoming connection is normal server and marked as backup + server then it will use us as backup router. We'll disable the + connection until it is allowed to be used. */ + if (ctx->conn_type == SILC_SOCKET_TYPE_SERVER && conn->backup_router) + SILC_SET_DISABLED(sock); +#endif + /* Check whether this connection is to be our primary router connection - if we dont' already have the primary route. */ + if we do not already have the primary route. */ if (server->standalone && ctx->conn_type == SILC_SOCKET_TYPE_ROUTER) { if (silc_server_config_is_primary_route(server->config) && !conn->initiator) @@ -1372,8 +1406,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final) silc_packet_context_free(ctx->packet); if (ctx->ske) silc_ske_free(ctx->ske); - if (ctx->dest_id) - silc_free(ctx->dest_id); + silc_free(ctx->dest_id); silc_free(ctx); sock->protocol = NULL; } @@ -1468,16 +1501,6 @@ SILC_TASK_CALLBACK(silc_server_packet_process) SILC_LOG_DEBUG(("Premature EOF from connection %d", sock->sock)); SILC_SET_DISCONNECTING(sock); - /* If the closed connection was our primary router connection the - start re-connecting phase. */ - if (!server->standalone && sock->type == SILC_SOCKET_TYPE_ROUTER && - sock == server->router->connection) - silc_schedule_task_add(server->schedule, 0, - silc_server_connect_to_router, - context, 1, 0, - SILC_TASK_TIMEOUT, - SILC_TASK_PRI_NORMAL); - if (sock->user_data) silc_server_free_sock_user_data(server, sock); silc_server_close_connection(server, sock); @@ -1568,6 +1591,10 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) goto out; } + /* If entry is disabled ignore what we got. */ + if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED) + goto out; + if (ret == 0) { /* Parse the packet. Packet type is returned. */ ret = silc_packet_parse(packet); @@ -1625,12 +1652,16 @@ SILC_TASK_CALLBACK(silc_server_packet_parse_real) 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); } } out: - /* silc_buffer_clear(sock->inbuf); */ silc_packet_context_free(packet); silc_free(parse_ctx); } @@ -2078,6 +2109,19 @@ void silc_server_packet_parse_type(SilcServer server, } break; + case SILC_PACKET_FTP: + /* Ignored */ + break; + + 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); + break; + default: SILC_LOG_ERROR(("Incorrect packet type %d, packet dropped", type)); break; @@ -2208,19 +2252,7 @@ void silc_server_free_client_data(SilcServer server, /* If there is pending outgoing data for the client then purge it to the network before removing the client entry. */ - if (sock && SILC_IS_OUTBUF_PENDING(sock) && - (SILC_IS_DISCONNECTED(sock) == FALSE)) { - server->stat.packets_sent++; - - if (sock->outbuf->data - sock->outbuf->head) - silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head); - - silc_packet_send(sock, TRUE); - - SILC_SET_CONNECTION_FOR_INPUT(server->schedule, sock->sock); - SILC_UNSET_OUTBUF_PENDING(sock); - silc_buffer_clear(sock->outbuf); - } + silc_server_packet_queue_purge(server, sock); /* Send SIGNOFF notify to routers. */ if (notify && !server->standalone && server->router) @@ -2275,27 +2307,83 @@ void silc_server_free_sock_user_data(SilcServer server, case SILC_SOCKET_TYPE_ROUTER: { SilcServerEntry user_data = (SilcServerEntry)sock->user_data; - - /* 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); + SilcServerEntry backup_router = NULL; /* If this was our primary router connection then we're lost to the outside world. */ if (server->router == user_data) { - server->id_entry->router = NULL; - server->router = NULL; - server->standalone = TRUE; + backup_router = silc_server_backup_get(server); + + /* 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); + + server->id_entry->router = NULL; + server->router = NULL; + server->standalone = TRUE; + 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)); + server->id_entry->router = backup_router; + server->router = backup_router; + server->router_connect = time(0); + if (server->server_type == SILC_BACKUP_ROUTER) { + server->server_type = SILC_ROUTER; + + /* We'll need to constantly try to reconnect to the primary + router so that we'll see when it comes back online. */ + silc_server_backup_reconnect(server, sock->ip, sock->port, + silc_server_backup_connected, + NULL); + } + + /* Mark this connection as replaced */ + silc_server_backup_replaced_add(server, user_data->id, + backup_router); + } + } + + 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); + } else { + /* 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); } /* Free the server entry */ + silc_server_backup_del(server, user_data); + if (user_data->id) + silc_server_backup_replaced_del(server, user_data->id); silc_idlist_del_data(user_data); silc_idlist_del_server(server->local_list, user_data); server->stat.my_servers--; server->stat.servers--; if (server->server_type == SILC_ROUTER) server->stat.cell_servers--; + + if (backup_router) { + /* 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) + silc_server_announce_servers(server, FALSE, time(0) - 300); + + /* Announce our clients and channels to the router */ + silc_server_announce_clients(server, time(0) - 300); + silc_server_announce_channels(server, time(0) - 300); + } break; } default: @@ -2319,346 +2407,6 @@ void silc_server_free_sock_user_data(SilcServer server, sock->user_data = NULL; } -/* Removes the client from channels and possibly removes the channels - as well. After removing those channels that exist, their channel - keys are regnerated. This is called only by the function - silc_server_remove_clients_by_server. */ - -static void silc_server_remove_clients_channels(SilcServer server, - SilcSocketConnection sock, - SilcClientEntry client, - SilcHashTable channels) -{ - SilcChannelEntry channel; - SilcChannelClientEntry chl; - SilcHashTableList htl; - SilcBuffer clidp; - - SILC_LOG_DEBUG(("Start")); - - if (!client || !client->id) - return; - - clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); - - /* Remove the client from all channels. The client is removed from - the channels' user list. */ - silc_hash_table_list(client->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chl)) { - channel = chl->channel; - - /* Remove channel from client's channel list */ - silc_hash_table_del(client->channels, channel); - - /* Remove channel if there is no users anymore */ - if (server->server_type == SILC_ROUTER && - silc_hash_table_count(channel->user_list) < 2) { - - if (silc_hash_table_find(channels, channel, NULL, NULL)) - silc_hash_table_del(channels, channel); - - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - - if (!silc_idlist_del_channel(server->local_list, channel)) - silc_idlist_del_channel(server->global_list, channel); - server->stat.my_channels--; - continue; - } - - /* Remove client from channel's client list */ - silc_hash_table_del(channel->user_list, chl->client); - silc_free(chl); - server->stat.my_chanclients--; - - /* 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_SERVER && channel->global_users && - chl->client->router && !silc_server_channel_has_global(channel)) - channel->global_users = FALSE; - - /* 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. */ - if (server->server_type == SILC_SERVER && - !silc_server_channel_has_local(channel)) { - - if (silc_hash_table_find(channels, channel, NULL, NULL)) - silc_hash_table_del(channels, channel); - - if (channel->rekey) - silc_schedule_task_del_by_context(server->schedule, channel->rekey); - - if (channel->founder_key) { - /* The founder auth data exists, do not remove the channel entry */ - SilcChannelClientEntry chl2; - SilcHashTableList htl2; - - channel->id = NULL; - - silc_hash_table_list(channel->user_list, &htl2); - while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) { - silc_hash_table_del(chl2->client->channels, channel); - silc_hash_table_del(channel->user_list, chl2->client); - silc_free(chl2); - } - continue; - } - - /* Remove the channel entry */ - if (!silc_idlist_del_channel(server->local_list, channel)) - silc_idlist_del_channel(server->global_list, channel); - server->stat.my_channels--; - continue; - } - - /* Add the channel to the the channels list to regenerate the - channel key */ - if (!silc_hash_table_find(channels, channel, NULL, NULL)) - silc_hash_table_add(channels, channel, channel); - } - - silc_buffer_free(clidp); -} - -/* This function is used to remove all client entries by the server `entry'. - This is called when the connection is lost to the server. In this case - we must invalidate all the client entries owned by the server `entry'. - If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is - distributed to our local clients. */ - -int silc_server_remove_clients_by_server(SilcServer server, - SilcServerEntry entry, - int server_signoff) -{ - SilcIDCacheList list = NULL; - SilcIDCacheEntry id_cache = NULL; - SilcClientEntry client = NULL; - SilcBuffer idp; - SilcClientEntry *clients = NULL; - uint32 clients_c = 0; - unsigned char **argv = NULL; - uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0; - SilcHashTableList htl; - SilcChannelEntry channel; - SilcHashTable channels; - int i; - - SILC_LOG_DEBUG(("Start")); - - /* Allocate the hash table that holds the channels that require - channel key re-generation after we've removed this server's clients - from the channels. */ - channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL, - NULL, NULL, TRUE); - - if (server_signoff) { - idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER); - argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); - argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1)); - argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1)); - argv[argc] = silc_calloc(idp->len, sizeof(*argv[0])); - memcpy(argv[argc], idp->data, idp->len); - argv_lens[argc] = idp->len; - argv_types[argc] = argc + 1; - argc++; - silc_buffer_free(idp); - } - - if (silc_idcache_get_all(server->local_list->clients, &list)) { - - if (silc_idcache_list_first(list, &id_cache)) { - while (id_cache) { - client = (SilcClientEntry)id_cache->context; - if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { - if (!silc_idcache_list_next(list, &id_cache)) - break; - else - continue; - } - - if (client->router != entry) { - if (server_signoff && client->connection) { - clients = silc_realloc(clients, - sizeof(*clients) * (clients_c + 1)); - clients[clients_c] = client; - clients_c++; - } - - if (!silc_idcache_list_next(list, &id_cache)) - break; - else - continue; - } - - if (server_signoff) { - idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); - argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); - argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * - (argc + 1)); - argv_types = silc_realloc(argv_types, sizeof(*argv_types) * - (argc + 1)); - argv[argc] = silc_calloc(idp->len, sizeof(*argv[0])); - memcpy(argv[argc], idp->data, idp->len); - argv_lens[argc] = idp->len; - argv_types[argc] = argc + 1; - argc++; - silc_buffer_free(idp); - } - - /* Remove the client entry */ - silc_server_remove_clients_channels(server, NULL, client, channels); - silc_idlist_del_client(server->local_list, client); - - if (!silc_idcache_list_next(list, &id_cache)) - break; - } - } - silc_idcache_list_free(list); - } - - if (silc_idcache_get_all(server->global_list->clients, &list)) { - - if (silc_idcache_list_first(list, &id_cache)) { - while (id_cache) { - client = (SilcClientEntry)id_cache->context; - if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { - if (!silc_idcache_list_next(list, &id_cache)) - break; - else - continue; - } - - if (client->router != entry) { - if (server_signoff && client->connection) { - clients = silc_realloc(clients, - sizeof(*clients) * (clients_c + 1)); - clients[clients_c] = client; - clients_c++; - } - - if (!silc_idcache_list_next(list, &id_cache)) - break; - else - continue; - } - - if (server_signoff) { - idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); - argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); - argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * - (argc + 1)); - argv_types = silc_realloc(argv_types, sizeof(*argv_types) * - (argc + 1)); - argv[argc] = silc_calloc(idp->len, sizeof(*argv[0])); - memcpy(argv[argc], idp->data, idp->len); - argv_lens[argc] = idp->len; - argv_types[argc] = argc + 1; - argc++; - silc_buffer_free(idp); - } - - /* Remove the client entry */ - silc_server_remove_clients_channels(server, NULL, client, channels); - silc_idlist_del_client(server->global_list, client); - - if (!silc_idcache_list_next(list, &id_cache)) - break; - } - } - silc_idcache_list_free(list); - } - - /* Send the SERVER_SIGNOFF notify */ - if (server_signoff) { - SilcBuffer args; - - /* Send SERVER_SIGNOFF notify to our primary router */ - if (!server->standalone && server->router && - server->router != entry) { - args = silc_argument_payload_encode(1, argv, argv_lens, - argv_types); - silc_server_send_notify_args(server, - server->router->connection, - server->server_type == SILC_SERVER ? - FALSE : TRUE, - SILC_NOTIFY_TYPE_SERVER_SIGNOFF, - argc, args); - silc_buffer_free(args); - } - - args = silc_argument_payload_encode(argc, argv, argv_lens, - argv_types); - /* Send to local clients */ - for (i = 0; i < clients_c; i++) { - silc_server_send_notify_args(server, clients[i]->connection, - FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF, - argc, args); - } - - silc_free(clients); - silc_buffer_free(args); - for (i = 0; i < argc; i++) - silc_free(argv[i]); - silc_free(argv); - silc_free(argv_lens); - silc_free(argv_types); - } - - /* We must now re-generate the channel key for all channels that had - this server's client(s) on the channel. As they left the channel we - must re-generate the channel key. */ - silc_hash_table_list(channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&channel)) { - if (!silc_server_create_channel_key(server, channel, 0)) - return FALSE; - - /* Do not send the channel key if private channel key mode is set */ - if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) - continue; - - silc_server_send_channel_key(server, NULL, channel, - server->server_type == SILC_ROUTER ? - FALSE : !server->standalone); - } - silc_hash_table_free(channels); - - return TRUE; -} - -/* Checks whether given channel has global users. If it does this returns - TRUE and FALSE if there is only locally connected clients on the channel. */ - -int silc_server_channel_has_global(SilcChannelEntry channel) -{ - SilcChannelClientEntry chl; - SilcHashTableList htl; - - silc_hash_table_list(channel->user_list, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chl)) { - if (chl->client->router) - return TRUE; - } - - return FALSE; -} - -/* Checks whether given channel has locally connected users. If it does this - returns TRUE and FALSE if there is not one locally connected client. */ - -int silc_server_channel_has_local(SilcChannelEntry channel) -{ - SilcChannelClientEntry chl; - SilcHashTableList htl; - - silc_hash_table_list(channel->user_list, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chl)) { - if (!chl->client->router) - return TRUE; - } - - return FALSE; -} - /* Removes client from all channels it has joined. This is used when client connection is disconnected. If the client on a channel is last, the channel is removed as well. This sends the SIGNOFF notify types. */ @@ -2704,18 +2452,19 @@ void silc_server_remove_from_channels(SilcServer server, /* Remove client from channel's client list */ silc_hash_table_del(channel->user_list, chl->client); - silc_free(chl); - server->stat.my_chanclients--; /* 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_SERVER && channel->global_users && + if (server->server_type != SILC_ROUTER && channel->global_users && chl->client->router && !silc_server_channel_has_global(channel)) channel->global_users = FALSE; + silc_free(chl); + server->stat.my_chanclients--; + /* 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. */ - if (server->server_type == SILC_SERVER && + if (server->server_type != SILC_ROUTER && !silc_server_channel_has_local(channel)) { /* Notify about leaving client if this channel has global users. */ if (notify && channel->global_users) @@ -2822,18 +2571,19 @@ int silc_server_remove_from_one_channel(SilcServer server, /* Remove client from channel's client list */ silc_hash_table_del(channel->user_list, chl->client); - silc_free(chl); - server->stat.my_chanclients--; /* 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_SERVER && channel->global_users && + if (server->server_type != SILC_ROUTER && channel->global_users && chl->client->router && !silc_server_channel_has_global(channel)) channel->global_users = FALSE; + silc_free(chl); + server->stat.my_chanclients--; + /* 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. */ - if (server->server_type == SILC_SERVER && + if (server->server_type != SILC_ROUTER && !silc_server_channel_has_local(channel)) { /* Notify about leaving client if this channel has global users. */ if (notify && channel->global_users) @@ -2879,23 +2629,6 @@ int silc_server_remove_from_one_channel(SilcServer server, return TRUE; } -/* Returns TRUE if the given client is on the channel. FALSE if not. - This works because we assure that the user list on the channel is - always in up to date thus we can only check the channel list from - `client' which is faster than checking the user list from `channel'. */ - -int silc_server_client_on_channel(SilcClientEntry client, - SilcChannelEntry channel) -{ - if (!client || !channel) - return FALSE; - - if (silc_hash_table_find(client->channels, channel, NULL, NULL)) - return TRUE; - - return FALSE; -} - /* Timeout callback. This is called if connection is idle or for some other reason is not responding within some period of time. This disconnects the remote end. */ @@ -3293,7 +3026,8 @@ void silc_server_perform_heartbeat(SilcSocketConnection sock, static void silc_server_announce_get_servers(SilcServer server, SilcServerEntry remote, SilcIDList id_list, - SilcBuffer *servers) + SilcBuffer *servers, + unsigned long creation_time) { SilcIDCacheList list; SilcIDCacheEntry id_cache; @@ -3307,8 +3041,10 @@ static void silc_server_announce_get_servers(SilcServer server, entry = (SilcServerEntry)id_cache->context; /* Do not announce the one we've sending our announcements and - do not announce ourself. */ - if (entry == remote || entry == server->id_entry) { + do not announce ourself. Also check the creation time if it's + provided. */ + if ((entry == remote) || (entry == server->id_entry) || + (creation_time && entry->data.created < creation_time)) { if (!silc_idcache_list_next(list, &id_cache)) break; continue; @@ -3335,9 +3071,12 @@ static void silc_server_announce_get_servers(SilcServer server, } /* This function is used by router to announce existing servers to our - primary router when we've connected to it. */ + primary router when we've connected to it. If `creation_time' is non-zero + then only the servers that has been created after the `creation_time' + will be announced. */ -void silc_server_announce_servers(SilcServer server) +void silc_server_announce_servers(SilcServer server, bool global, + unsigned long creation_time) { SilcBuffer servers = NULL; @@ -3345,11 +3084,14 @@ void silc_server_announce_servers(SilcServer server) /* Get servers in local list */ silc_server_announce_get_servers(server, server->router, - server->local_list, &servers); + server->local_list, &servers, + creation_time); - /* Get servers in global list */ - silc_server_announce_get_servers(server, server->router, - server->global_list, &servers); + if (global) + /* Get servers in global list */ + silc_server_announce_get_servers(server, server->router, + server->global_list, &servers, + creation_time); if (servers) { silc_buffer_push(servers, servers->data - servers->head); @@ -3369,7 +3111,8 @@ void silc_server_announce_servers(SilcServer server) static void silc_server_announce_get_clients(SilcServer server, SilcIDList id_list, - SilcBuffer *clients) + SilcBuffer *clients, + unsigned long creation_time) { SilcIDCacheList list; SilcIDCacheEntry id_cache; @@ -3382,6 +3125,12 @@ static void silc_server_announce_get_clients(SilcServer server, while (id_cache) { client = (SilcClientEntry)id_cache->context; + if (creation_time && client->data.created < creation_time) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + continue; + } + idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); *clients = silc_buffer_realloc(*clients, @@ -3403,9 +3152,12 @@ static void silc_server_announce_get_clients(SilcServer server, } /* This function is used to announce our existing clients to our router - when we've connected to it. */ + when we've connected to it. If `creation_time' is non-zero then only + the clients that has been created after the `creation_time' will be + announced. */ -void silc_server_announce_clients(SilcServer server) +void silc_server_announce_clients(SilcServer server, + unsigned long creation_time) { SilcBuffer clients = NULL; @@ -3413,12 +3165,12 @@ void silc_server_announce_clients(SilcServer server) /* Get clients in local list */ silc_server_announce_get_clients(server, server->local_list, - &clients); + &clients, creation_time); /* As router we announce our global list as well */ if (server->server_type == SILC_ROUTER) silc_server_announce_get_clients(server, server->global_list, - &clients); + &clients, creation_time); if (clients) { silc_buffer_push(clients, clients->data - clients->head); @@ -3519,7 +3271,8 @@ void silc_server_announce_get_channels(SilcServer server, SilcBuffer *channel_users, SilcBuffer **channel_users_modes, uint32 *channel_users_modes_c, - SilcChannelID ***channel_ids) + SilcChannelID ***channel_ids, + unsigned long creation_time) { SilcIDCacheList list; SilcIDCacheEntry id_cache; @@ -3529,6 +3282,7 @@ void silc_server_announce_get_channels(SilcServer server, uint16 name_len; int len; int i = *channel_users_modes_c; + bool announce; SILC_LOG_DEBUG(("Start")); @@ -3537,26 +3291,34 @@ void silc_server_announce_get_channels(SilcServer server, if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { channel = (SilcChannelEntry)id_cache->context; - + + if (creation_time && channel->created < creation_time) + announce = FALSE; + else + announce = TRUE; + cid = silc_id_id2str(channel->id, SILC_ID_CHANNEL); id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL); name_len = strlen(channel->channel_name); - len = 4 + name_len + id_len + 4; - *channels = - silc_buffer_realloc(*channels, - (*channels ? (*channels)->truelen + len : len)); - silc_buffer_pull_tail(*channels, - ((*channels)->end - (*channels)->data)); - silc_buffer_format(*channels, - SILC_STR_UI_SHORT(name_len), - SILC_STR_UI_XNSTRING(channel->channel_name, - name_len), - SILC_STR_UI_SHORT(id_len), - SILC_STR_UI_XNSTRING(cid, id_len), - SILC_STR_UI_INT(channel->mode), - SILC_STR_END); - silc_buffer_pull(*channels, len); + if (announce) { + len = 4 + name_len + id_len + 4; + *channels = + silc_buffer_realloc(*channels, + (*channels ? (*channels)->truelen + + len : len)); + silc_buffer_pull_tail(*channels, + ((*channels)->end - (*channels)->data)); + silc_buffer_format(*channels, + SILC_STR_UI_SHORT(name_len), + SILC_STR_UI_XNSTRING(channel->channel_name, + name_len), + SILC_STR_UI_SHORT(id_len), + SILC_STR_UI_XNSTRING(cid, id_len), + SILC_STR_UI_INT(channel->mode), + SILC_STR_END); + silc_buffer_pull(*channels, len); + } *channel_users_modes = silc_realloc(*channel_users_modes, sizeof(**channel_users_modes) * @@ -3584,9 +3346,13 @@ void silc_server_announce_get_channels(SilcServer server, /* This function is used to announce our existing channels to our router when we've connected to it. This also announces the users on the - channels to the router. */ + channels to the router. If the `creation_time' is non-zero only the + channels that was created after the `creation_time' are announced. + Note that the channel users are still announced even if the `creation_time' + was provided. */ -void silc_server_announce_channels(SilcServer server) +void silc_server_announce_channels(SilcServer server, + unsigned long creation_time) { SilcBuffer channels = NULL, channel_users = NULL; SilcBuffer *channel_users_modes = NULL; @@ -3600,14 +3366,14 @@ void silc_server_announce_channels(SilcServer server) &channels, &channel_users, &channel_users_modes, &channel_users_modes_c, - &channel_ids); + &channel_ids, creation_time); /* Get channels and channel users in global list */ silc_server_announce_get_channels(server, server->global_list, &channels, &channel_users, &channel_users_modes, &channel_users_modes_c, - &channel_ids); + &channel_ids, creation_time); if (channels) { silc_buffer_push(channels, channels->data - channels->head); @@ -3857,7 +3623,7 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server, /* Destination belongs to someone not in this server. If we are normal server our action is to send the packet to our router. */ - if (server->server_type == SILC_SERVER && !server->standalone) { + if (server->server_type != SILC_ROUTER && !server->standalone) { silc_free(id); if (idata) *idata = (SilcIDListData)server->router; @@ -4031,6 +3797,7 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final) 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_protocol_cancel(protocol, server->schedule); silc_protocol_free(protocol); sock->protocol = NULL; @@ -4042,6 +3809,10 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final) return; } + /* 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); + /* Cleanup */ silc_protocol_free(protocol); sock->protocol = NULL;