leaks
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
index 3c29c229deaea7da9c7b86364b178cc286800ccf..30ad925a7508582cb754526e8e05abf64b4102e3 100644 (file)
  * @file topology/gnunet-daemon-topology.c
  * @brief code for maintaining the mesh topology
  * @author Christian Grothoff
- *
- * TODO:
- * - make sure that PEERINFO *also* notifies us when a HELLO expires
- *   (otherwise the 'soft state' of this module does not work nicely)
- * - CORE API's connect/disconnect API is not nice (we think we are
- *   connected when we are not; disconnect using bandwidth-limiting
- *   does not really work and is not nice)
- * 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>
@@ -93,7 +79,7 @@ struct PeerList
    * Our handle for the request to connect to this peer; NULL if no
    * such request is pending.
    */
-  struct GNUNET_CORE_TransmitHandle *connect_req;  
+  struct GNUNET_CORE_PeerRequestHandle *connect_req;  
 
   /**
    * Pointer to the HELLO message of this peer; can be NULL.
@@ -106,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?
    */
@@ -116,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?
@@ -164,8 +161,8 @@ struct DisconnectList
   /**
    * Our request handle.
    */
-  struct GNUNET_CORE_InformationRequestContext *rh;
-
+  struct GNUNET_TRANSPORT_BlacklistRequest *rh;
+  
   /**
    * Peer we tried to disconnect.
    */
@@ -258,23 +255,11 @@ static struct DisconnectList *disconnect_tail;
  * has completed.
  *
  * @param cls our 'struct DisconnectList'
- * @param peer NULL on error (then what?)
- * @param bpm_in set to the current bandwidth limit (receiving) for this peer
- * @param bpm_out set to the current bandwidth limit (sending) for this peer
- * @param latency current latency estimate, "FOREVER" if we have been
- *                disconnected
- * @param amount set to the amount that was actually reserved or unreserved
- * @param preference current traffic preference for the given peer
+ * @param tc unused
  */
 static void
 disconnect_done (void *cls,
-                const struct
-                GNUNET_PeerIdentity * peer,
-                unsigned int bpm_in,
-                unsigned int bpm_out,
-                struct GNUNET_TIME_Relative
-                latency, int amount,
-                unsigned long long preference)
+                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct DisconnectList *dl = cls;
 
@@ -286,61 +271,101 @@ disconnect_done (void *cls,
 
 
 /**
- * Force a disconnect from the specified peer.  This is currently done by
- * changing the bandwidth policy to 0 bytes per second.  
- * FIXME: maybe we want a nicer CORE API for both connect and disconnect...
- * FIXME: this policy change is never undone; how do we reconnect ever?
+ * Force a disconnect from the specified peer. 
  */
 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,
                               disconnect_tail,
                               dl);
-  dl->rh = GNUNET_CORE_peer_get_info (sched, cfg,
-                                     peer,
-                                     GNUNET_TIME_UNIT_FOREVER_REL,
-                                     0,
-                                     0,
-                                     0,
-                                     &disconnect_done,
-                                     dl);
+  dl->rh = GNUNET_TRANSPORT_blacklist (sched, cfg,                                             
+                                      peer,
+                                      GNUNET_TIME_UNIT_FOREVER_REL,
+                                      GNUNET_TIME_UNIT_FOREVER_REL,
+                                      &disconnect_done,
+                                      dl);
+}
+
+
+
+/**
+ * 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.
- * Transmits a 'DUMMY' message to trigger the session key exchange.
- * FIXME: this is an issue with the current CORE API.
  */
-static size_t
-ready_callback (void *cls,
-               size_t size, void *buf)
+static void
+connect_completed_callback (void *cls,
+                           const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct PeerList *pos = cls;
-  struct GNUNET_MessageHeader hdr;
 
   pos->connect_req = NULL;
-  if (buf == NULL)
-    {
-#if DEBUG_TOPOLOGY
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Core told us that our attempt to connect failed.\n");
-#endif
-      return 0;
-    }
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Sending dummy message to establish connection.\n");
-#endif
-  hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
-  hdr.type = htons (GNUNET_MESSAGE_TYPE_TOPOLOGY_DUMMY);
-  memcpy (buf, &hdr, sizeof (struct GNUNET_MessageHeader));
-  return sizeof (struct GNUNET_MessageHeader);
 }
 
 
@@ -361,13 +386,11 @@ attempt_connect (struct PeerList *pos)
              "Asking core to connect to `%s'\n",
              GNUNET_i2s (&pos->id));
 #endif
-  pos->connect_req = GNUNET_CORE_notify_transmit_ready (handle,
-                                                       0 /* priority */,
-                                                       GNUNET_TIME_UNIT_MINUTES,
-                                                       &pos->id,
-                                                       sizeof(struct GNUNET_MessageHeader),
-                                                       &ready_callback,
-                                                       pos);
+  pos->connect_req = GNUNET_CORE_peer_request_connect (sched, cfg,
+                                                      GNUNET_TIME_UNIT_MINUTES,
+                                                      &pos->id,
+                                                      &connect_completed_callback,
+                                                      pos);
 }
 
 
@@ -477,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_notify_transmit_ready_cancel (pos->connect_req);            
+     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);
 }
 
@@ -623,11 +651,15 @@ reschedule_hellos (struct PeerList *peer)
  *
  * @param cls closure
  * @param peer peer identity this notification is about
+ * @param latency reported latency of the connection with 'other'
+ * @param distance reported distance (DV) to 'other' 
  */
 static void 
 connect_notify (void *cls,
                const struct
-               GNUNET_PeerIdentity * peer)
+               GNUNET_PeerIdentity * peer,
+               struct GNUNET_TIME_Relative latency,
+               uint32_t distance)
 {
   struct PeerList *pos;
 
@@ -650,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;
        }
     }
@@ -661,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);
 }
 
@@ -685,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;
     }
@@ -807,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",
@@ -815,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)
@@ -873,6 +910,7 @@ process_peer (void *cls,
     }
   consider_for_advertising (hello);
   pos = find_peer (peer);  
+  GNUNET_assert (NULL != pos);
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Considering connecting to peer `%s'\n",
@@ -922,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);
     }
@@ -1094,6 +1133,8 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
  * @param other the other peer involved (sender or receiver, NULL
  *        for loopback messages where we are both sender and receiver)
  * @param message the actual HELLO message
+ * @param latency reported latency of the connection with 'other'
+ * @param distance reported distance (DV) to 'other' 
  * @return GNUNET_OK to keep the connection open,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
@@ -1101,7 +1142,9 @@ static int
 handle_encrypted_hello (void *cls,
                        const struct GNUNET_PeerIdentity * other,
                        const struct GNUNET_MessageHeader *
-                       message)
+                       message,
+                       struct GNUNET_TIME_Relative latency,
+                       uint32_t distance)
 {
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -1215,7 +1258,7 @@ cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
       GNUNET_CONTAINER_DLL_remove (disconnect_head,
                                   disconnect_tail,
                                   dl);
-      GNUNET_CORE_peer_get_info_cancel (dl->rh);
+      GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
       GNUNET_free (dl);
     }
 }
@@ -1288,6 +1331,7 @@ run (void *cls,
                                GNUNET_TIME_UNIT_FOREVER_REL,
                                NULL,
                                &core_init,
+                               NULL,
                                &connect_notify,
                                &disconnect_notify,
                                NULL, GNUNET_NO,