+/* Packet retransmission callback. */
+
+SILC_TASK_CALLBACK(silc_ske_packet_send_retry)
+{
+ SilcSKE ske = context;
+
+ if (ske->retry_count++ >= SILC_SKE_RETRY_COUNT) {
+ SILC_LOG_DEBUG(("Retry limit reached, packet was lost"));
+ ske->retry_count = 0;
+ ske->retry_timer = SILC_SKE_RETRY_MIN;
+ silc_free(ske->retrans.data);
+ ske->retrans.data = NULL;
+ ske->status = SILC_SKE_STATUS_TIMEOUT;
+ if (ske->responder)
+ silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
+ else
+ silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+ silc_fsm_continue_sync(&ske->fsm);
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Retransmitting packet"));
+ silc_ske_packet_send(ske, ske->retrans.type, ske->retrans.flags,
+ ske->retrans.data, ske->retrans.data_len);
+}
+
+/* Sends SILC packet. Handles retransmissions with UDP streams. */
+
+static SilcBool silc_ske_packet_send(SilcSKE ske,
+ SilcPacketType type,
+ SilcPacketFlags flags,
+ const unsigned char *data,
+ SilcUInt32 data_len)
+{
+ SilcBool ret;
+
+ /* Send the packet */
+ ret = silc_packet_send(ske->stream, type, flags, data, data_len);
+
+ if (silc_packet_stream_is_udp(ske->stream) &&
+ type != SILC_PACKET_FAILURE) {
+ silc_free(ske->retrans.data);
+ ske->retrans.type = type;
+ ske->retrans.flags = flags;
+ ske->retrans.data = silc_memdup(data, data_len);
+ ske->retrans.data_len = data_len;
+ if (ske->retrans.data) {
+ SILC_LOG_DEBUG(("Installing retransmission timer %d secs",
+ ske->retry_timer));
+ silc_schedule_task_add_timeout(ske->schedule, silc_ske_packet_send_retry,
+ ske, ske->retry_timer, 0);
+ }
+ ske->retry_timer = ((ske->retry_timer * SILC_SKE_RETRY_MUL) +
+ (silc_rng_get_rn16(ske->rng) % SILC_SKE_RETRY_RAND));
+ }
+
+ return ret;
+}
+