leaks
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
index 3bcc297044643e66072f9287ec47806c2e9d9a26..30ad925a7508582cb754526e8e05abf64b4102e3 100644 (file)
  * @file topology/gnunet-daemon-topology.c
  * @brief code for maintaining the mesh topology
  * @author Christian Grothoff
- *
- * OPTIMIZATIONS:
- * - move code to use hash table instead of linked list
- * - instead of periodically discarding blacklisted entries,
- *   simply add task that is triggered at the right time (earlier free,
- *   more balanced load)
- * - check if new HELLO learned is different from old HELLO
- *   before resetting entire state!
  */
 
 #include <stdlib.h>
@@ -100,6 +92,12 @@ struct PeerList
    */
   struct GNUNET_CONTAINER_BloomFilter *filter;
 
+  /**
+   * Our request handle for *whitelisting* this peer (NULL if
+   * no whitelisting request is pending).
+   */
+  struct GNUNET_TRANSPORT_BlacklistRequest *wh;
+
   /**
    * Is this peer listed here because he is a friend?
    */
@@ -110,6 +108,11 @@ struct PeerList
    */
   int is_connected;
 
+  /**
+   * Are we currently blocking this peer (via blacklist)?
+   */
+  int is_blocked;
+
   /**
    * Until what time should we not try to connect again
    * to this peer?
@@ -269,13 +272,19 @@ disconnect_done (void *cls,
 
 /**
  * Force a disconnect from the specified peer. 
- * FIXME: this policy change is never undone; how do we reconnect ever?
  */
 static void
-force_disconnect (const struct GNUNET_PeerIdentity *peer)
+force_disconnect (struct PeerList *pl)
 {
+  const struct GNUNET_PeerIdentity *peer = &pl->id;
   struct DisconnectList *dl;
 
+  if (NULL != pl->wh)
+    {
+      GNUNET_TRANSPORT_blacklist_cancel (pl->wh);
+      pl->wh = NULL;
+    }
+  pl->is_blocked = GNUNET_YES;
   dl = GNUNET_malloc (sizeof (struct DisconnectList));
   dl->peer = *peer;
   GNUNET_CONTAINER_DLL_insert (disconnect_head,
@@ -290,6 +299,63 @@ force_disconnect (const struct GNUNET_PeerIdentity *peer)
 }
 
 
+
+/**
+ * Function called once our request to 'whitelist' a peer
+ * has completed.
+ *
+ * @param cls our 'struct PeerList'
+ * @param tc unused
+ */
+static void
+whitelist_done (void *cls,
+               const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct PeerList *pl = cls;
+
+  pl->wh = NULL;
+}
+
+
+/**
+ * Whitelist all peers that we blacklisted; we've passed
+ * the minimum number of friends.
+ */
+static void
+whitelist_peers ()
+{
+  struct PeerList *pl;
+  struct DisconnectList *dl;
+
+  /* first, cancel all blacklisting requests */
+  while (NULL != (dl = disconnect_head))
+    {
+      GNUNET_CONTAINER_DLL_remove (disconnect_head,
+                                  disconnect_tail,
+                                  dl);
+      GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
+      GNUNET_free (dl);
+    }
+  /* then, specifically whitelist all peers that we
+     know to have blacklisted */
+  pl = peers;
+  while (pl != NULL)
+    {
+      if (pl->is_blocked)
+       {
+         pl->wh = GNUNET_TRANSPORT_blacklist (sched, cfg,                                              
+                                              &pl->id,
+                                              GNUNET_TIME_UNIT_ZERO,
+                                              GNUNET_TIME_UNIT_FOREVER_REL,
+                                              &whitelist_done,
+                                              pl);
+         pl->is_blocked = GNUNET_NO;
+       }
+      pl = pl->next;
+    }
+}
+
+
 /**
  * Function called by core when our attempt to connect succeeded.
  */
@@ -434,11 +500,16 @@ free_peer (struct PeerList *peer)
      prev->next = pos->next;
    if (pos->hello_req != NULL)
      GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
+   if (pos->wh != NULL)
+     GNUNET_TRANSPORT_blacklist_cancel (pos->wh);
    if (pos->connect_req != NULL)
      GNUNET_CORE_peer_request_connect_cancel (pos->connect_req);             
    if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
      GNUNET_SCHEDULER_cancel (sched,
                              pos->hello_delay_task);
+   GNUNET_free_non_null (pos->hello);   
+   if (pos->filter != NULL)
+     GNUNET_CONTAINER_bloomfilter_free (peer->filter);
    GNUNET_free (pos);
 }
 
@@ -611,7 +682,7 @@ connect_notify (void *cls,
                      "Connection to `%s' is forbidden, forcing disconnect!\n",
                      GNUNET_i2s (peer));
 #endif       
-         force_disconnect (&pos->id);
+         force_disconnect (pos);
          return;
        }
     }
@@ -622,7 +693,12 @@ connect_notify (void *cls,
     }
   pos->is_connected = GNUNET_YES;
   if (pos->is_friend)
-    friend_count++;
+    {
+      if ( (friend_count == minimum_friend_count - 1) &&
+          (GNUNET_YES != friends_only) )       
+       whitelist_peers ();       
+      friend_count++;
+    }
   reschedule_hellos (pos);
 }
 
@@ -646,7 +722,7 @@ drop_non_friends ()
                      "Connection to `%s' is not from a friend, forcing disconnect!\n",
                      GNUNET_i2s (&pos->id));
 #endif       
-         force_disconnect (&pos->id);
+         force_disconnect (pos);
        }
       pos = pos->next;
     }
@@ -768,7 +844,6 @@ consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
   if (peer == NULL)
     peer = make_peer (&pid, hello, GNUNET_NO);
   // FIXME: check if 'hello' is any different from peer->hello?
-  GNUNET_free_non_null (peer->hello);
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Found `%s' from peer `%s' for advertising\n",
@@ -776,6 +851,7 @@ consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
              GNUNET_i2s (&pid));
 #endif 
   size = GNUNET_HELLO_size (hello);
+  GNUNET_free_non_null (peer->hello);
   peer->hello = GNUNET_malloc (size);
   memcpy (peer->hello, hello, size);
   if (peer->filter != NULL)
@@ -884,6 +960,7 @@ discard_old_blacklist_entries (void *cls,
       next = pos->next;
       if ( (GNUNET_NO == pos->is_friend) &&
           (GNUNET_NO == pos->is_connected) &&
+          (GNUNET_NO == pos->is_blocked) &&
           (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
        free_peer (pos);
     }