+/* 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,
+ SilcPacketStream sock,
+ SilcBool failure)
+{
+ unsigned char data[4];
+
+ SILC_LOG_DEBUG(("Sending START_USE (%s)",
+ failure ? "failure" : "success"));
+
+ if (failure) {
+ SILC_PUT32_MSB(SILC_SERVER_BACKUP_START_USE, data);
+ silc_server_packet_send(server, sock, SILC_PACKET_FAILURE, 0,
+ data, 4);
+ } else {
+ data[0] = SILC_SERVER_BACKUP_START_USE;
+ data[1] = 0;
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_RESUME_ROUTER, 0,
+ data, 2);
+ }
+}
+
+/* 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,
+ SilcPacketStream sock)
+{
+ unsigned char data[4];
+
+ SILC_LOG_DEBUG(("Sending REPLACED"));
+
+ data[0] = SILC_SERVER_BACKUP_REPLACED;
+ data[1] = 0;
+ silc_server_packet_send(server, sock,
+ SILC_PACKET_RESUME_ROUTER, 0,
+ data, 2);
+}
+
+
+/************************ Backup Resuming Protocol **************************/
+
+/* Timeout callback for protocol */
+
+SILC_TASK_CALLBACK(silc_server_backup_timeout)
+{
+ SilcServerBackupProtocolContext ctx = context;
+ SilcServer server = app_context;
+
+ SILC_LOG_INFO(("Timeout occurred during backup resuming protocol"));
+ ctx->timeout = TRUE;
+ ctx->error = TRUE;
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_protocol_backup_done, context,
+ 0, 0);
+}
+
+/* Callback to start the protocol as responder */
+
+SILC_TASK_CALLBACK(silc_server_backup_responder_start)
+{
+ SilcServerBackupProtocolContext proto_ctx = context;
+ SilcPacketStream sock = proto_ctx->sock;
+ SilcIDListData idata = silc_packet_get_context(sock);
+ SilcServer server = app_context;
+
+ /* If other protocol is executing at the same time, start with timeout. */
+ if (idata->sconn->op) {
+ SILC_LOG_DEBUG(("Other protocol is executing, wait for it to finish"));
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_responder_start,
+ proto_ctx, 2, 0);
+ return;
+ }
+
+ /* Register protocol timeout */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_backup_timeout,
+ proto_ctx, 30, 0);
+
+ /* Run the backup resuming protocol */
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_protocol_backup,
+ proto_ctx, 0, 0);
+}
+
+/* Callback to send START_USE to backup to check whether using backup
+ is ok. */
+
+SILC_TASK_CALLBACK(silc_server_backup_check_status)
+{
+ SilcPacketStream 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_packet_stream_unref(sock);
+}
+
+typedef struct {
+ SilcServer server;
+ SilcPacketStream sock;
+ SilcPacket 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. */
+ SilcPacketStream primary = SILC_PRIMARY_ROUTE(pc->server);
+
+ SILC_LOG_DEBUG(("PING timeout, primary is down"));
+
+ if (primary) {
+ silc_server_free_sock_user_data(pc->server, primary, NULL);
+ 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_packet_free(pc->packet);
+ }
+
+ silc_packet_stream_unref(pc->sock);
+ silc_free(pc);
+}
+