- implementation for mantis 0002485
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
index d09de78ed62ad977951b39c0a638c7659ba60f54..3578b9e9be3e9b1c03841a8c9a664114c0044245 100644 (file)
 #include "gnunet_util_lib.h"
 
 
+/**
+ * Minimum required delay between calls to GNUNET_TRANSPORT_try_connect.
+ */
+#define MAX_CONNECT_FREQUENCY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
+
 /**
  * For how long do we blacklist a peer after a failed connection
  * attempt?  This is the baseline factor which is then multiplied by
@@ -121,6 +126,11 @@ struct Peer
    */
   GNUNET_SCHEDULER_TaskIdentifier hello_delay_task;
 
+  /**
+   * Task for issuing GNUNET_TRANSPORT_try_connect for this peer.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier attempt_connect_task;
+
   /**
    * ID of task we use to clear peers from the greylist.
    */
@@ -187,6 +197,11 @@ static struct GNUNET_STATISTICS_Handle *stats;
  */
 static struct GNUNET_TRANSPORT_Blacklist *blacklist;
 
+/**
+ * When can we next ask transport to create a connection?
+ */
+static struct GNUNET_TIME_Absolute next_connect_attempt;
+
 /**
  * Task scheduled to try to add peers.
  */
@@ -301,16 +316,19 @@ is_connection_allowed (struct Peer *peer)
  * @return GNUNET_YES (always: continue to iterate)
  */
 static int
-free_peer (void *cls, const GNUNET_HashCode * pid, void *value)
+free_peer (void *cls, const struct GNUNET_HashCode * pid, void *value)
 {
   struct Peer *pos = value;
 
+  GNUNET_break (GNUNET_NO == pos->is_connected);
   GNUNET_break (GNUNET_OK ==
                 GNUNET_CONTAINER_multihashmap_remove (peers, pid, pos));
   if (pos->hello_req != NULL)
     GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
   if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
+  if (pos->attempt_connect_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (pos->attempt_connect_task);
   if (pos->greylist_clean_task != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_SCHEDULER_cancel (pos->greylist_clean_task);
   GNUNET_free_non_null (pos->hello);
@@ -377,6 +395,51 @@ attempt_connect (struct Peer *pos)
 }
 
 
+/**
+ * Try to connect to the specified peer.
+ *
+ * @param cls peer to connect to
+ * @param tc scheduler context
+ */
+static void
+do_attempt_connect (void *cls,
+                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Peer *pos = cls;
+  struct GNUNET_TIME_Relative delay;
+
+  pos->attempt_connect_task = GNUNET_SCHEDULER_NO_TASK;
+  if (GNUNET_YES == pos->is_connected)
+    return;
+  delay = GNUNET_TIME_absolute_get_remaining (next_connect_attempt);
+  if (delay.rel_value > 0)
+  {
+    pos->attempt_connect_task = GNUNET_SCHEDULER_add_delayed (delay,
+                                                             &do_attempt_connect,
+                                                             pos);
+    return;
+  }
+  next_connect_attempt = GNUNET_TIME_relative_to_absolute (MAX_CONNECT_FREQUENCY_DELAY);
+  attempt_connect (pos);
+}
+
+
+/**
+ * Schedule a task to try to connect to the specified peer.
+ *
+ * @param pos peer to connect to
+ */
+static void
+schedule_attempt_connect (struct Peer *pos)
+{
+  if (GNUNET_SCHEDULER_NO_TASK != pos->attempt_connect_task)
+    return;
+  pos->attempt_connect_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (next_connect_attempt),
+                                                           &do_attempt_connect,
+                                                           pos);
+}
+
+
 /**
  * Discard peer entries for greylisted peers
  * where the greylisting has expired.
@@ -394,7 +457,7 @@ remove_from_greylist (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   rem = GNUNET_TIME_absolute_get_remaining (pos->greylisted_until);
   if (rem.rel_value == 0)
   {
-    attempt_connect (pos);
+    schedule_attempt_connect (pos);
   }
   else
   {
@@ -510,7 +573,7 @@ struct FindAdvHelloContext
  * @return GNUNET_YES (continue iteration)
  */
 static int
-find_advertisable_hello (void *cls, const GNUNET_HashCode * pid, void *value)
+find_advertisable_hello (void *cls, const struct GNUNET_HashCode * pid, void *value)
 {
   struct FindAdvHelloContext *fah = cls;
   struct Peer *pos = value;
@@ -596,7 +659,7 @@ schedule_next_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * @return GNUNET_YES (always)
  */
 static int
-reschedule_hellos (void *cls, const GNUNET_HashCode * pid, void *value)
+reschedule_hellos (void *cls, const struct GNUNET_HashCode * pid, void *value)
 {
   struct Peer *peer = value;
   struct Peer *skip = cls;
@@ -646,7 +709,7 @@ connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer,
   GNUNET_STATISTICS_set (stats, gettext_noop ("# peers connected"),
                          connection_count, GNUNET_NO);
   pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
-  if (pos == NULL)
+  if (NULL == pos)
   {
     pos = make_peer (peer, NULL, GNUNET_NO);
     GNUNET_break (GNUNET_OK == is_connection_allowed (pos));
@@ -680,11 +743,11 @@ connect_notify (void *cls, const struct GNUNET_PeerIdentity *peer,
  * @return GNUNET_YES (continue to iterate)
  */
 static int
-try_add_peers (void *cls, const GNUNET_HashCode * pid, void *value)
+try_add_peers (void *cls, const struct GNUNET_HashCode * pid, void *value)
 {
   struct Peer *pos = value;
 
-  attempt_connect (pos);
+  schedule_attempt_connect (pos);
   return GNUNET_YES;
 }
 
@@ -704,6 +767,7 @@ add_peer_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   GNUNET_CONTAINER_multihashmap_iterate (peers, &try_add_peers, NULL);
 }
 
+
 /**
  * Method called whenever a peer disconnects.
  *
@@ -721,7 +785,7 @@ disconnect_notify (void *cls, const struct GNUNET_PeerIdentity *peer)
               "Core told us that we disconnected from `%s'\n",
               GNUNET_i2s (peer));
   pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
-  if (pos == NULL)
+  if (NULL == pos)
   {
     GNUNET_break (0);
     return;
@@ -808,7 +872,7 @@ consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
   if (GNUNET_NO == have_address)
     return;                     /* no point in advertising this one... */
   peer = GNUNET_CONTAINER_multihashmap_get (peers, &pid.hashPubKey);
-  if (peer == NULL)
+  if (NULL == peer)
   {
     peer = make_peer (&pid, hello, GNUNET_NO);
   }
@@ -882,7 +946,7 @@ process_peer (void *cls, const struct GNUNET_PeerIdentity *peer,
         GNUNET_CONTAINER_bloomfilter_free (pos->filter);
         pos->filter = NULL;
       }
-      if ((!pos->is_connected) && (!pos->is_friend) &&
+      if ((GNUNET_NO == pos->is_connected) && (GNUNET_NO == pos->is_friend) &&
           (0 ==
            GNUNET_TIME_absolute_get_remaining (pos->
                                                greylisted_until).rel_value))
@@ -909,7 +973,7 @@ process_peer (void *cls, const struct GNUNET_PeerIdentity *peer,
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Considering connecting to peer `%s'\n",
               GNUNET_i2s (peer));
-  attempt_connect (pos);
+  schedule_attempt_connect (pos);
 }
 
 
@@ -1105,7 +1169,7 @@ handle_encrypted_hello (void *cls, const struct GNUNET_PeerIdentity *other,
   GNUNET_STATISTICS_update (stats, gettext_noop ("# HELLO messages received"),
                             1, GNUNET_NO);
   peer = GNUNET_CONTAINER_multihashmap_get (peers, &pid.hashPubKey);
-  if (peer == NULL)
+  if (NULL == peer)
   {
     if ((GNUNET_YES == friends_only) || (friend_count < minimum_friend_count))
       return GNUNET_OK;
@@ -1185,21 +1249,22 @@ cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
     peerinfo_notify = NULL;
   }
-  if (GNUNET_SCHEDULER_NO_TASK != add_task)
-  {
-    GNUNET_SCHEDULER_cancel (add_task);
-    add_task = GNUNET_SCHEDULER_NO_TASK;
-  }
   GNUNET_TRANSPORT_disconnect (transport);
   transport = NULL;
-  GNUNET_CONTAINER_multihashmap_iterate (peers, &free_peer, NULL);
-  GNUNET_CONTAINER_multihashmap_destroy (peers);
   if (handle != NULL)
   {
     GNUNET_CORE_disconnect (handle);
     handle = NULL;
   }
   whitelist_peers ();
+  if (GNUNET_SCHEDULER_NO_TASK != add_task)
+  {
+    GNUNET_SCHEDULER_cancel (add_task);
+    add_task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  GNUNET_CONTAINER_multihashmap_iterate (peers, &free_peer, NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (peers);
+  peers = NULL;
   if (stats != NULL)
   {
     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
@@ -1254,7 +1319,7 @@ run (void *cls, char *const *args, const char *cfgfile,
     blacklist = GNUNET_TRANSPORT_blacklist (cfg, &blacklist_check, NULL);
   transport = GNUNET_TRANSPORT_connect (cfg, NULL, NULL, NULL, NULL, NULL);
   handle =
-      GNUNET_CORE_connect (cfg, 1, NULL, &core_init, &connect_notify,
+      GNUNET_CORE_connect (cfg, NULL, &core_init, &connect_notify,
                            &disconnect_notify, NULL, GNUNET_NO, NULL, GNUNET_NO,
                            handlers);
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleaning_task,
@@ -1291,6 +1356,9 @@ main (int argc, char *const *argv)
   };
   int ret;
 
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+
   ret =
       (GNUNET_OK ==
        GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-topology",