+/* Send the START_USE indication to remote connection. If `failure' is
+ TRUE then this sends SILC_PACKET_FAILURE. Otherwise it sends
+ SILC_PACKET_RESUME_ROUTER. */
+
+void silc_server_backup_send_start_use(SilcServer server,
+ SilcSocketConnection sock,
+ bool failure)
+{
+ unsigned char data[4];
+
+ SILC_LOG_DEBUG(("Sending START_USE (%s) to %s",
+ failure ? "failure" : "success", sock->ip));
+
+ if (failure) {
+ SILC_PUT32_MSB(SILC_SERVER_BACKUP_START_USE, data);
+ silc_server_packet_send(server, sock, SILC_PACKET_FAILURE, 0,
+ data, 4, FALSE);
+ } else {
+ data[0] = SILC_SERVER_BACKUP_START_USE;
+ data[1] = 0;
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_RESUME_ROUTER, 0,
+ data, 2, FALSE);
+ }
+}
+
+/* Send the REPLACED indication to remote router. This is send by the
+ primary router (remote router) of the primary router that came back
+ online. This is not sent by backup router or any other server. */
+
+void silc_server_backup_send_replaced(SilcServer server,
+ SilcSocketConnection sock)
+{
+ unsigned char data[4];
+
+ SILC_LOG_DEBUG(("Sending REPLACED (%s) to %s", sock->ip));
+
+ data[0] = SILC_SERVER_BACKUP_REPLACED;
+ data[1] = 0;
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_RESUME_ROUTER, 0,
+ data, 2, FALSE);
+}
+
+
+/************************ Backup Resuming Protocol **************************/
+
+/* Timeout callback for protocol */
+
+SILC_TASK_CALLBACK(silc_server_backup_timeout)
+{
+ SilcProtocol protocol = context;
+ SilcServerBackupProtocolContext ctx = protocol->context;
+ SilcServer server = app_context;
+
+ SILC_LOG_INFO(("Timeout occurred during backup resuming protocol"));
+ ctx->timeout = TRUE;
+ silc_protocol_cancel(protocol, server->schedule);
+ protocol->state = SILC_PROTOCOL_STATE_ERROR;
+ silc_protocol_execute_final(protocol, server->schedule);
+}
+
+/* Callback to start the protocol as responder */
+
+SILC_TASK_CALLBACK(silc_server_backup_responder_start)
+{
+ SilcServerBackupProtocolContext proto_ctx = context;
+ SilcSocketConnection sock = proto_ctx->sock;
+ SilcServer server = app_context;
+
+ /* If other protocol is executing at the same time, start with timeout. */
+ if (sock->protocol) {
+ SILC_LOG_DEBUG(("Other protocol is executing, wait for it to finish"));
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_backup_responder_start,
+ proto_ctx, 2, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ return;
+ }
+
+ /* Run the backup resuming protocol */
+ silc_protocol_alloc(SILC_PROTOCOL_SERVER_BACKUP,
+ &sock->protocol, proto_ctx,
+ silc_server_protocol_backup_done);
+ silc_protocol_execute(sock->protocol, server->schedule, 0, 0);
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_backup_timeout,
+ sock->protocol, 30, 0, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+}
+
+/* Callback to send START_USE to backup to check whether using backup
+ is ok. */
+
+SILC_TASK_CALLBACK(silc_server_backup_check_status)
+{
+ SilcSocketConnection sock = context;
+ SilcServer server = app_context;
+
+ /* Check whether we are still using backup */
+ if (!server->backup_primary)
+ return;
+
+ silc_server_backup_send_start_use(server, sock, FALSE);
+ silc_socket_free(sock); /* unref */
+}
+
+typedef struct {
+ SilcServer server;
+ SilcSocketConnection sock;
+ SilcPacketContext *packet;
+} *SilcServerBackupPing;
+
+/* PING command reply callback */
+
+void silc_server_backup_ping_reply(void *context, void *reply)
+{
+ SilcServerBackupPing pc = context;
+ SilcServerCommandReplyContext cmdr = reply;
+
+ if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) {
+ /* Timeout error occurred, the primary is really down. */
+ SilcSocketConnection primary = SILC_PRIMARY_ROUTE(pc->server);
+
+ SILC_LOG_DEBUG(("PING timeout, primary is down"));
+
+ if (primary) {
+ if (primary->user_data)
+ silc_server_free_sock_user_data(pc->server, primary, NULL);
+ SILC_SET_DISCONNECTING(primary);
+ silc_server_close_connection(pc->server, primary);
+ }
+
+ /* Reprocess the RESUME_ROUTER packet */
+ silc_server_backup_resume_router(pc->server, pc->sock, pc->packet);
+ } else {
+ /* The primary is not down, refuse to serve the server as primary */
+ SILC_LOG_DEBUG(("PING received, primary is up"));
+ silc_server_backup_send_start_use(pc->server, pc->sock, TRUE);
+ }
+
+ silc_socket_free(pc->sock);
+ silc_packet_context_free(pc->packet);
+ silc_free(pc);
+}
+