+/**
+ * Iterate over validation entries and free them.
+ *
+ * @param cls (unused)
+ * @param key peer identity (unused)
+ * @param value a 'struct ValidationEntry' to clean up
+ * @return GNUNET_YES (continue to iterate)
+ */
+static int
+cleanup_validation_entry (void *cls, const struct GNUNET_HashCode * key, void *value)
+{
+ struct ValidationEntry *ve = value;
+
+ if (NULL != ve->bc)
+ {
+ GST_blacklist_test_cancel (ve->bc);
+ ve->bc = NULL;
+ }
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (validation_map,
+ &ve->pid.hashPubKey, ve));
+ GNUNET_HELLO_address_free (ve->address);
+ if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (ve->timeout_task);
+ ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != ve->revalidation_task)
+ {
+ GNUNET_SCHEDULER_cancel (ve->revalidation_task);
+ ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ GNUNET_free (ve);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Address validation cleanup task. Assesses if the record is no
+ * longer valid and then possibly triggers its removal.
+ *
+ * @param cls the 'struct ValidationEntry'
+ * @param tc scheduler context (unused)
+ */
+static void
+timeout_hello_validation (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ValidationEntry *ve = cls;
+ struct GNUNET_TIME_Absolute max;
+ struct GNUNET_TIME_Relative left;
+
+ ve->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ max = GNUNET_TIME_absolute_max (ve->valid_until, ve->revalidation_block);
+ left = GNUNET_TIME_absolute_get_remaining (max);
+ if (left.rel_value > 0)
+ {
+ /* should wait a bit longer */
+ ve->timeout_task =
+ GNUNET_SCHEDULER_add_delayed (left, &timeout_hello_validation, ve);
+ return;
+ }
+ GNUNET_STATISTICS_update (GST_stats,
+ gettext_noop ("# address records discarded"), 1,
+ GNUNET_NO);
+ cleanup_validation_entry (NULL, &ve->pid.hashPubKey, ve);
+}
+
+
+/**
+ * Function called with the result from blacklisting.
+ * Send a PING to the other peer if a communication is allowed.
+ *
+ * @param cls our 'struct ValidationEntry'
+ * @param pid identity of the other peer
+ * @param result GNUNET_OK if the connection is allowed, GNUNET_NO if not
+ */
+static void
+transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid,
+ int result)
+{
+ struct ValidationEntry *ve = cls;
+ struct TransportPingMessage ping;
+ struct GNUNET_TRANSPORT_PluginFunctions *papi;
+ const struct GNUNET_MessageHeader *hello;
+ ssize_t ret;
+ size_t tsize;
+ size_t slen;
+ uint16_t hsize;
+
+ ve->bc = NULL;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting plain PING to `%s' %s %s\n",
+ GNUNET_i2s (pid), GST_plugins_a2s (ve->address), ve->address->transport_name);
+
+ slen = strlen (ve->address->transport_name) + 1;
+ hello = GST_hello_get ();
+ hsize = ntohs (hello->size);
+ tsize =
+ sizeof (struct TransportPingMessage) + ve->address->address_length +
+ slen + hsize;
+
+ ping.header.size =
+ htons (sizeof (struct TransportPingMessage) +
+ ve->address->address_length + slen);
+ ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
+ ping.challenge = htonl (ve->challenge);
+ ping.target = *pid;
+
+ if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("Not transmitting `%s' with `%s', message too big (%u bytes!). This should not happen.\n"),
+ "HELLO", "PING", (unsigned int) tsize);
+ /* message too big (!?), get rid of HELLO */
+ hsize = 0;
+ tsize =
+ sizeof (struct TransportPingMessage) + ve->address->address_length +
+ slen + hsize;
+ }
+ {
+ char message_buf[tsize];
+
+ /* build message with structure:
+ * [HELLO][TransportPingMessage][Transport name][Address] */
+ memcpy (message_buf, hello, hsize);
+ memcpy (&message_buf[hsize], &ping, sizeof (struct TransportPingMessage));
+ memcpy (&message_buf[sizeof (struct TransportPingMessage) + hsize],
+ ve->address->transport_name, slen);
+ memcpy (&message_buf[sizeof (struct TransportPingMessage) + slen + hsize],
+ ve->address->address, ve->address->address_length);
+ papi = GST_plugins_find (ve->address->transport_name);
+ if (papi == NULL)
+ ret = -1;
+ else
+ {
+ GNUNET_assert (papi->send != NULL);
+ GNUNET_assert (papi->get_session != NULL);
+ struct Session * session = papi->get_session(papi->cls, ve->address);
+
+ if (session != NULL)
+ {
+ ret = papi->send (papi->cls, session,
+ message_buf, tsize,
+ PING_PRIORITY, ACCEPTABLE_PING_DELAY,
+ NULL, NULL);
+ }
+ else
+ {
+ /* Could not get a valid session */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could not get a valid session for `%s' %s\n",
+ GNUNET_i2s (pid), GST_plugins_a2s (ve->address));
+ ret = -1;
+ }
+ }
+ }
+ if (-1 != ret)
+ {
+ ve->send_time = GNUNET_TIME_absolute_get ();
+ GNUNET_STATISTICS_update (GST_stats,
+ gettext_noop
+ ("# PING without HELLO messages sent"), 1,
+ GNUNET_NO);
+ ve->expecting_pong = GNUNET_YES;
+ }
+}
+
+
+/**
+ * Do address validation again to keep address valid.
+ *
+ * @param cls the 'struct ValidationEntry'
+ * @param tc scheduler context (unused)
+ */
+static void
+revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct ValidationEntry *ve = cls;
+ struct GNUNET_TIME_Relative canonical_delay;
+ struct GNUNET_TIME_Relative delay;
+ struct GST_BlacklistCheck *bc;
+ uint32_t rdelay;
+
+ ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK;
+ delay = GNUNET_TIME_absolute_get_remaining (ve->revalidation_block);
+ /* How long until we can possibly permit the next PING? */
+ canonical_delay =
+ (ve->in_use ==
+ GNUNET_YES) ? CONNECTED_PING_FREQUENCY
+ : ((GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value >
+ 0) ? VALIDATED_PING_FREQUENCY : UNVALIDATED_PING_KEEPALIVE);
+ if (delay.rel_value > canonical_delay.rel_value * 2)
+ {
+ /* situation changed, recalculate delay */
+ delay = canonical_delay;
+ ve->revalidation_block = GNUNET_TIME_relative_to_absolute (delay);
+ }
+ if (delay.rel_value > 0)
+ {
+ /* should wait a bit longer */
+ ve->revalidation_task =
+ GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve);
+ return;
+ }
+ ve->revalidation_block = GNUNET_TIME_relative_to_absolute (canonical_delay);
+
+ /* schedule next PINGing with some extra random delay to avoid synchronous re-validations */
+ rdelay =
+ GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ canonical_delay.rel_value);
+ delay =
+ GNUNET_TIME_relative_add (canonical_delay,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, rdelay));
+ ve->revalidation_task =
+ GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve);
+
+ /* start PINGing by checking blacklist */
+ GNUNET_STATISTICS_update (GST_stats,
+ gettext_noop ("# address revalidations started"), 1,
+ GNUNET_NO);
+ bc = GST_blacklist_test_allowed (&ve->pid, ve->address->transport_name,
+ &transmit_ping_if_allowed, ve);
+ if (NULL != bc)
+ ve->bc = bc; /* only set 'bc' if 'transmit_ping_if_allowed' was not already
+ * called... */
+}
+
+