-doxygen
[oweals/gnunet.git] / src / transport / gnunet-service-transport_blacklist.c
index 09aa43452088287617629a3d2e5fd781d172e8d1..87d7539295ba91874ad8f56eeee1bea177811820 100644 (file)
 /**
  * @file transport/gnunet-service-transport_blacklist.c
  * @brief blacklisting implementation
- * @author Christian Grothoff
+ * @author Christian Grothoff, Matthias Wachs
+ * @details This is the blacklisting component of transport service. With
+ * blacklisting it is possible to deny connections to specific peers of
+ * to use a specific plugin to a specific peer. Peers can be blacklisted using
+ * the configuration or a blacklist client can be asked.
+ *
+ * To blacklist peers using the configuration you have to add a section to your
+ * configuration containing the peer id of the peer to blacklist and the plugin
+ * if required.
+ *
+ * Example:
+ * To blacklist connections to P565... on peer AG2P... using tcp add:
+ * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
+ * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G = tcp
+ *
+ * To blacklist connections to P565... on peer AG2P... using all plugins add:
+ * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
+ * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G =
+ *
+ * You can also add a blacklist client usign the blacklist api. On a blacklist
+ * check, blacklisting first checks internally if the peer is blacklisted and
+ * if not, it asks the blacklisting clients. Clients are asked if it is OK to
+ * connect to a peer ID, the plugin is omitted.
+ *
+ * On blacklist check for (peer, plugin)
+ * - Do we have a local blacklist entry for this peer and this plugin?
+ *   - YES: disallow connection
+ * - Do we have a local blacklist entry for this peer and all plugins?
+ *   - YES: disallow connection
+ * - Does one of the clients disallow?
+ *   - YES: disallow connection
+ *
  */
 #include "platform.h"
 #include "gnunet-service-transport.h"
@@ -29,7 +60,6 @@
 #include "gnunet-service-transport_neighbours.h"
 #include "transport.h"
 
-
 /**
  * Size of the blacklist hash map.
  */
@@ -39,7 +69,7 @@
 /**
  * Context we use when performing a blacklist check.
  */
-struct BlacklistCheck;
+struct GST_BlacklistCheck;
 
 
 /**
@@ -64,9 +94,15 @@ struct Blacklisters
   struct GNUNET_SERVER_Client *client;
 
   /**
-   * Blacklist check that we're currently performing.
+   * Blacklist check that we're currently performing (or NULL
+   * if we're performing one that has been cancelled).
+   */
+  struct GST_BlacklistCheck *bc;
+
+  /**
+   * Set to GNUNET_YES if we're currently waiting for a reply.
    */
-  struct BlacklistCheck *bc;
+  int waiting_for_reply;
 
 };
 
@@ -75,18 +111,18 @@ struct Blacklisters
 /**
  * Context we use when performing a blacklist check.
  */
-struct BlacklistCheck
+struct GST_BlacklistCheck
 {
 
   /**
    * This is a linked list.
    */
-  struct BlacklistCheck *next;
+  struct GST_BlacklistCheck *next;
 
   /**
    * This is a linked list.
    */
-  struct BlacklistCheck *prev;
+  struct GST_BlacklistCheck *prev;
 
   /**
    * Peer being checked.
@@ -107,7 +143,7 @@ struct BlacklistCheck
    * Current transmission request handle for this client, or NULL if no
    * request is pending.
    */
-  struct GNUNET_CONNECTION_TransmitHandle *th;
+  struct GNUNET_SERVER_TransmitHandle *th;
 
   /**
    * Our current position in the blacklisters list.
@@ -125,12 +161,12 @@ struct BlacklistCheck
 /**
  * Head of DLL of active blacklisting queries.
  */
-static struct BlacklistCheck *bc_head;
+static struct GST_BlacklistCheck *bc_head;
 
 /**
  * Tail of DLL of active blacklisting queries.
  */
-static struct BlacklistCheck *bc_tail;
+static struct GST_BlacklistCheck *bc_tail;
 
 /**
  * Head of DLL of blacklisting clients.
@@ -146,7 +182,7 @@ static struct Blacklisters *bl_tail;
  * Hashmap of blacklisted peers.  Values are of type 'char *' (transport names),
  * can be NULL if we have no static blacklist.
  */
-static struct GNUNET_CONTAINER_MultiHashMap *blacklist;
+static struct GNUNET_CONTAINER_MultiPeerMap *blacklist;
 
 
 /**
@@ -156,8 +192,7 @@ static struct GNUNET_CONTAINER_MultiHashMap *blacklist;
  * @param tc unused
  */
 static void
-do_blacklist_check (void *cls,
-                   const struct GNUNET_SCHEDULER_TaskContext *tc);
+do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 
 /**
@@ -168,215 +203,105 @@ do_blacklist_check (void *cls,
  * @param client identification of the client
  */
 static void
-client_disconnect_notification (void *cls,
-                                struct GNUNET_SERVER_Client *client)
+client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client)
 {
   struct Blacklisters *bl;
-  struct BlacklistCheck *bc;
+  struct GST_BlacklistCheck *bc;
 
   if (client == NULL)
     return;
   for (bl = bl_head; bl != NULL; bl = bl->next)
+  {
+    if (bl->client != client)
+      continue;
+    for (bc = bc_head; bc != NULL; bc = bc->next)
     {
-      if (bl->client != client)
-       continue;
-      for (bc = bc_head; bc != NULL; bc = bc->next)
-       {
-         if (bc->bl_pos != bl)
-           continue;
-         bc->bl_pos = bl->next;
-         if (bc->th != NULL)
-           {
-             GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
-             bc->th = NULL;
-           }
-         if (bc->task == GNUNET_SCHEDULER_NO_TASK)
-           bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
-                                                bc);
-         break;
-       }
-      GNUNET_CONTAINER_DLL_remove (bl_head,
-                                  bl_tail,
-                                  bl);
-      GNUNET_SERVER_client_drop (bl->client);
-      GNUNET_free (bl);
+      if (bc->bl_pos != bl)
+        continue;
+      bc->bl_pos = bl->next;
+      if (bc->th != NULL)
+      {
+        GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
+        bc->th = NULL;
+      }
+      if (bc->task == GNUNET_SCHEDULER_NO_TASK)
+        bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
       break;
     }
+    GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
+    GNUNET_SERVER_client_drop (bl->client);
+    GNUNET_free (bl);
+    break;
+  }
 }
 
 
 /**
- * Read the blacklist file, containing transport:peer entries.
- * Provided the transport is loaded, set up hashmap with these
- * entries to blacklist peers by transport.
+ * Function to iterate over options in the blacklisting section for a peer.
  *
+ * @param cls closure
+ * @param section name of the section
+ * @param option name of the option
+ * @param value value of the option
  */
 static void
-read_blacklist_file ()
+blacklist_cfg_iter (void *cls, const char *section,
+                   const char *option,
+                   const char *value)
 {
-  char *fn;
-  char *data;
-  size_t pos;
-  size_t colon_pos;
-  int tsize;
-  struct GNUNET_PeerIdentity pid;
-  struct stat frstat;
-  struct GNUNET_CRYPTO_HashAsciiEncoded enc;
-  unsigned int entries_found;
-  char *transport_name;
-
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (GST_cfg,
-                                               "TRANSPORT",
-                                               "BLACKLIST_FILE",
-                                               &fn))
-    {
-#if DEBUG_TRANSPORT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Option `%s' in section `%s' not specified!\n",
-                  "BLACKLIST_FILE",
-                  "TRANSPORT");
-#endif
-      return;
-    }
-  if (GNUNET_OK != GNUNET_DISK_file_test (fn))
-    GNUNET_DISK_fn_write (fn, NULL, 0, 
-                         GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
-  if (0 != STAT (fn, &frstat))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  _("Could not read blacklist file `%s'\n"), 
-                 fn);
-      GNUNET_free (fn);
-      return;
-    }
-  if (frstat.st_size == 0)
-    {
-#if DEBUG_TRANSPORT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  _("Blacklist file `%s' is empty.\n"),
-                  fn);
-#endif
-      GNUNET_free (fn);
-      return;
-    }
-  /* FIXME: use mmap */
-  data = GNUNET_malloc_large (frstat.st_size);
-  GNUNET_assert(data != NULL);
-  if (frstat.st_size !=
-      GNUNET_DISK_fn_read (fn, data, frstat.st_size))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  _("Failed to read blacklist from `%s'\n"), 
-                 fn);
-      GNUNET_free (fn);
-      GNUNET_free (data);
-      return;
-    }
-  entries_found = 0;
-  pos = 0;
-  while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
-    pos++;
-  while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
-         (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
-    {
-      colon_pos = pos;
-      while ( (colon_pos < frstat.st_size) && 
-             (data[colon_pos] != ':') && 
-             (! isspace ( (unsigned char) data[colon_pos])) )
-        colon_pos++;
-      if (colon_pos >= frstat.st_size)
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                      _("Syntax error in blacklist file at offset %llu, giving up!\n"),
-                      (unsigned long long) colon_pos);
-          GNUNET_free (fn);
-          GNUNET_free (data);
-          return;
-        }
-
-      if (isspace( (unsigned char) data[colon_pos]))
+  unsigned int *res = cls;
+  struct GNUNET_PeerIdentity peer;
+  char *plugs;
+  char *pos;
+
+  if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (option,
+                                                                 strlen (option),
+                                                                 &peer.public_key))
+    return;
+
+  if ((NULL == value) || (0 == strcmp(value, "")))
+  {
+    /* Blacklist whole peer */
+    GST_blacklist_add_peer (&peer, NULL);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               _("Adding blacklisting entry for peer `%s'\n"), GNUNET_i2s (&peer));
+  }
+  else
+  {
+    plugs = GNUNET_strdup (value);
+    for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                    _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
-                    (unsigned long long) colon_pos);
-        pos = colon_pos;
-        while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
-          pos++;
-        continue;
+       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                   _("Adding blacklisting entry for peer `%s':`%s'\n"),
+                   GNUNET_i2s (&peer), pos);
+       GST_blacklist_add_peer (&peer, pos);
       }
-      tsize = colon_pos - pos;
-      if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) || (tsize == 0))
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                      _("Syntax error in blacklist file at offset %llu, giving up!\n"),
-                      (unsigned long long) colon_pos);
-          GNUNET_free (fn);
-          GNUNET_free (data);
-          return;
-        }
-
-      if (tsize < 1)
-        continue;
+    GNUNET_free (plugs);
+  }
+  (*res)++;
+}
 
-      transport_name = GNUNET_malloc(tsize + 1);
-      memcpy(transport_name, &data[pos], tsize);
-      pos = colon_pos + 1;
-#if DEBUG_TRANSPORT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Read transport name `%s' in blacklist file.\n",
-                  transport_name);
-#endif
-      memcpy (&enc, 
-             &data[pos],
-             sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
-      if (! isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                      _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
-                      (unsigned long long) pos);
-          pos++;
-          while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
-            pos++;
-          GNUNET_free_non_null(transport_name);
-          continue;
-        }
-      enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
-      if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                      _("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
-                      (unsigned long long) pos,
-                      &enc);
-        }
-      else
-        {
-          if (0 != memcmp (&pid,
-                           &GST_my_identity,
-                           sizeof (struct GNUNET_PeerIdentity)))
-            {
-              entries_found++;
-              GST_blacklist_add_peer (&pid,
-                                     transport_name);
-            }
-          else
-            {
-              GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                          _("Found myself `%s' in blacklist (useless, ignored)\n"),
-                          GNUNET_i2s (&pid));
-            }
-        }
-      pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
-      GNUNET_free_non_null(transport_name);
-      while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
-        pos++;
-    }
-  GNUNET_STATISTICS_update (GST_stats, 
-                           "# Transport entries blacklisted",
-                           entries_found,
-                           GNUNET_NO);
-  GNUNET_free (data);
-  GNUNET_free (fn);
+
+/**
+ * Read blacklist configuration
+ *
+ * @param cfg the configuration handle
+ * @param my_id my peer identity
+ */
+static void
+read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                             const struct GNUNET_PeerIdentity *my_id)
+{
+  char cfg_sect[512];
+  unsigned int res = 0;
+
+  GNUNET_snprintf (cfg_sect,
+                  sizeof (cfg_sect),
+                  "transport-blacklist-%s",
+                  GNUNET_i2s_full (my_id));
+  GNUNET_CONFIGURATION_iterate_section_values (cfg, cfg_sect, &blacklist_cfg_iter, &res);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Loaded %u blacklisting entries from configuration\n", res);
 }
 
 
@@ -384,14 +309,19 @@ read_blacklist_file ()
  * Start blacklist subsystem.
  *
  * @param server server used to accept clients from
+ * @param cfg configuration handle
+ * @param my_id my peer id
  */
 void
-GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
+GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
+                    const struct GNUNET_CONFIGURATION_Handle *cfg,
+                    const struct GNUNET_PeerIdentity *my_id)
 {
-  read_blacklist_file ();
-  GNUNET_SERVER_disconnect_notify (server,
-                                  &client_disconnect_notification,
-                                  NULL);
+  GNUNET_assert (NULL != cfg);
+  GNUNET_assert (NULL != my_id);
+  read_blacklist_configuration (cfg, my_id);
+  GNUNET_SERVER_disconnect_notify (server, &client_disconnect_notification,
+                                   NULL);
 }
 
 
@@ -405,12 +335,12 @@ GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
  */
 static int
 free_blacklist_entry (void *cls,
-                     const GNUNET_HashCode *key,
+                     const struct GNUNET_PeerIdentity *key,
                      void *value)
 {
   char *be = value;
 
-  GNUNET_free (be);
+  GNUNET_free_non_null (be);
   return GNUNET_OK;
 }
 
@@ -422,49 +352,43 @@ void
 GST_blacklist_stop ()
 {
   if (NULL != blacklist)
-    {
-      GNUNET_CONTAINER_multihashmap_iterate (blacklist,
-                                            &free_blacklist_entry,
-                                            NULL);
-      GNUNET_CONTAINER_multihashmap_destroy (blacklist);
-      blacklist = NULL;
-    }
+  {
+    GNUNET_CONTAINER_multipeermap_iterate (blacklist, &free_blacklist_entry,
+                                           NULL);
+    GNUNET_CONTAINER_multipeermap_destroy (blacklist);
+    blacklist = NULL;
+  }
 }
 
 
 /**
  * Transmit blacklist query to the client.
  *
- * @param cls the 'struct BlacklistCheck'
+ * @param cls the 'struct GST_BlacklistCheck'
  * @param size number of bytes allowed
  * @param buf where to copy the message
  * @return number of bytes copied to buf
  */
 static size_t
-transmit_blacklist_message (void *cls,
-                           size_t size,
-                           void *buf)
+transmit_blacklist_message (void *cls, size_t size, void *buf)
 {
-  struct BlacklistCheck *bc = cls;
+  struct GST_BlacklistCheck *bc = cls;
   struct Blacklisters *bl;
   struct BlacklistMessage bm;
 
   bc->th = NULL;
   if (size == 0)
-    {
-      GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
-      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
-                                          bc);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 "Failed to send blacklist test for peer `%s' to client\n",
-                 GNUNET_i2s (&bc->peer));
-      return 0;
-    }
-#if DEBUG_TRANSPORT
+  {
+    GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
+    bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to send blacklist test for peer `%s' to client\n",
+                GNUNET_i2s (&bc->peer));
+    return 0;
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Sending blacklist test for peer `%s' to client\n",
-             GNUNET_i2s (&bc->peer));
-#endif
+              "Sending blacklist test for peer `%s' to client\n",
+              GNUNET_i2s (&bc->peer));
   bl = bc->bl_pos;
   bm.header.size = htons (sizeof (struct BlacklistMessage));
   bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
@@ -472,6 +396,7 @@ transmit_blacklist_message (void *cls,
   bm.peer = bc->peer;
   memcpy (buf, &bm, sizeof (bm));
   GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
+  bl->waiting_for_reply = GNUNET_YES;
   return sizeof (bm);
 }
 
@@ -479,39 +404,35 @@ transmit_blacklist_message (void *cls,
 /**
  * Perform next action in the blacklist check.
  *
- * @param cls the 'struct BlacklistCheck*'
+ * @param cls the 'struct GST_BlacklistCheck*'
  * @param tc unused
  */
 static void
-do_blacklist_check (void *cls,
-                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct BlacklistCheck *bc = cls;
+  struct GST_BlacklistCheck *bc = cls;
   struct Blacklisters *bl;
 
   bc->task = GNUNET_SCHEDULER_NO_TASK;
   bl = bc->bl_pos;
   if (bl == NULL)
-    {
-#if DEBUG_TRANSPORT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "No other blacklist clients active, will allow neighbour `%s'\n",
-                 GNUNET_i2s (&bc->peer));
-#endif
-      bc->cont (bc->cont_cls,
-               &bc->peer, 
-               GNUNET_OK);
-      GNUNET_free (bc);
-      return;
-    }
-  if (bl->bc != NULL)
-    return; /* someone else busy with this client */
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "No other blacklist clients active, will allow neighbour `%s'\n",
+                GNUNET_i2s (&bc->peer));
+    bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK);
+    GNUNET_CONTAINER_DLL_remove(bc_head, bc_tail, bc);
+    GNUNET_free (bc);
+    return;
+  }
+  if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO))
+    return;                     /* someone else busy with this client */
   bl->bc = bc;
-  bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client,
-                                               sizeof (struct BlacklistMessage),
-                                               GNUNET_TIME_UNIT_FOREVER_REL,
-                                               &transmit_blacklist_message,
-                                               bc);    
+  bc->th =
+      GNUNET_SERVER_notify_transmit_ready (bl->client,
+                                           sizeof (struct BlacklistMessage),
+                                           GNUNET_TIME_UNIT_FOREVER_REL,
+                                           &transmit_blacklist_message, bc);
 }
 
 
@@ -525,16 +446,14 @@ do_blacklist_check (void *cls,
  *                GNUNET_NO if we must shutdown the connection
  */
 static void
-confirm_or_drop_neighbour (void *cls,
-                          const struct GNUNET_PeerIdentity *peer,
-                          int allowed)
+confirm_or_drop_neighbour (void *cls, const struct GNUNET_PeerIdentity *peer,
+                           int allowed)
 {
   if (GNUNET_OK == allowed)
-    return; /* we're done */
+    return;                     /* we're done */
   GNUNET_STATISTICS_update (GST_stats,
-                           gettext_noop ("# disconnects due to blacklist"),
-                           1,
-                           GNUNET_NO);
+                            gettext_noop ("# disconnects due to blacklist"), 1,
+                            GNUNET_NO);
   GST_neighbours_force_disconnect (peer);
 }
 
@@ -546,7 +465,7 @@ struct TestConnectionContext
 {
   /**
    * Is this the first neighbour we're checking?
-   */ 
+   */
   int first;
 
   /**
@@ -561,37 +480,36 @@ struct TestConnectionContext
  * blacklisting client.
  *
  * @param cls the 'struct TestConnectionContest'
- * @param pid neighbour's identity
- * @param ats performance data
- * @param ats_count number of entries in ats (excluding 0-termination)
+ * @param neighbour neighbour's identity
+ * @param address the address
+ * @param bandwidth_in inbound quota in NBO
+ * @param bandwidth_out outbound quota in NBO
  */
 static void
-test_connection_ok (void *cls,
-                   const struct GNUNET_PeerIdentity *neighbour,
-                   const struct GNUNET_TRANSPORT_ATS_Information *ats,
-                   uint32_t ats_count)
+test_connection_ok (void *cls, const struct GNUNET_PeerIdentity *neighbour,
+                    const struct GNUNET_HELLO_Address *address,
+                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
+                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
 {
   struct TestConnectionContext *tcc = cls;
-  struct BlacklistCheck *bc;
+  struct GST_BlacklistCheck *bc;
 
-  bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
+  bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
   bc->peer = *neighbour;
   bc->cont = &confirm_or_drop_neighbour;
   bc->cont_cls = NULL;
   bc->bl_pos = tcc->bl;
   if (GNUNET_YES == tcc->first)
-    { 
-      /* all would wait for the same client, no need to
-        create more than just the first task right now */
-      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
-                                          bc);
-      tcc->first = GNUNET_NO;
-    }
+  {
+    /* all would wait for the same client, no need to
+     * create more than just the first task right now */
+    bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
+    tcc->first = GNUNET_NO;
+  }
 }
 
 
-
 /**
  * Initialize a blacklisting client.  We got a blacklist-init
  * message from this client, add him to the list of clients
@@ -602,24 +520,24 @@ test_connection_ok (void *cls,
  * @param message the blacklist-init message that was sent
  */
 void
-GST_blacklist_handle_init (void *cls,
-                          struct GNUNET_SERVER_Client *client,
-                          const struct GNUNET_MessageHeader *message)
+GST_blacklist_handle_init (void *cls, struct GNUNET_SERVER_Client *client,
+                           const struct GNUNET_MessageHeader *message)
 {
   struct Blacklisters *bl;
   struct TestConnectionContext tcc;
 
   bl = bl_head;
   while (bl != NULL)
+  {
+    if (bl->client == client)
     {
-      if (bl->client == client)
-       {
-         GNUNET_break (0);
-         GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-         return;
-       }
-      bl = bl->next;
+      GNUNET_break (0);
+      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+      return;
     }
+    bl = bl->next;
+  }
+  GNUNET_SERVER_client_mark_monitor (client);
   bl = GNUNET_malloc (sizeof (struct Blacklisters));
   bl->client = client;
   GNUNET_SERVER_client_keep (client);
@@ -628,8 +546,7 @@ GST_blacklist_handle_init (void *cls,
   /* confirm that all existing connections are OK! */
   tcc.bl = bl;
   tcc.first = GNUNET_YES;
-  GST_neighbours_iterate (&test_connection_ok,
-                         &tcc);
+  GST_neighbours_iterate (&test_connection_ok, &tcc);
 }
 
 
@@ -641,85 +558,88 @@ GST_blacklist_handle_init (void *cls,
  * @param message the blacklist-init message that was sent
  */
 void
-GST_blacklist_handle_reply (void *cls,
-                           struct GNUNET_SERVER_Client *client,
-                           const struct GNUNET_MessageHeader *message)
+GST_blacklist_handle_reply (void *cls, struct GNUNET_SERVER_Client *client,
+                            const struct GNUNET_MessageHeader *message)
 {
-  const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
+  const struct BlacklistMessage *msg =
+      (const struct BlacklistMessage *) message;
   struct Blacklisters *bl;
-  struct BlacklistCheck *bc;
+  struct GST_BlacklistCheck *bc;
 
   bl = bl_head;
-  while ( (bl != NULL) &&
-         (bl->client != client) )
+  while ((bl != NULL) && (bl->client != client))
     bl = bl->next;
   if (bl == NULL)
-    {
-#if DEBUG_TRANSPORT
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Blacklist client disconnected\n");
-#endif
-      /* FIXME: other error handling here!? */
-      GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
-      return;
-    }
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n");
+    /* FIXME: other error handling here!? */
+    GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+    return;
+  }
   bc = bl->bc;
   bl->bc = NULL;
-  if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
+  bl->waiting_for_reply = GNUNET_NO;
+  if (NULL != bc)
+  {
+    /* only run this if the blacklist check has not been
+     * cancelled in the meantime... */
+    if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
     {
-#if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Blacklist check failed, peer not allowed\n");
-#endif
+                  "Blacklist check failed, peer not allowed\n");
       bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
       GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
       GNUNET_free (bc);
     }
-  else
+    else
     {
-#if DEBUG_TRANSPORT
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Blacklist check succeeded, continuing with checks\n");
-#endif
+                  "Blacklist check succeeded, continuing with checks\n");
       bc->bl_pos = bc->bl_pos->next;
-      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
-                                          bc);
+      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
     }
+  }
   /* check if any other bc's are waiting for this blacklister */
-  bc = bc_head;
-  while (bc != NULL)
+  for (bc = bc_head; bc != NULL; bc = bc->next)
+    if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
     {
-      if ( (bc->bl_pos == bl) &&
-          (GNUNET_SCHEDULER_NO_TASK == bc->task) )
-       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
-                                            bc);
-      bc = bc->next;
+      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
+      break;
     }
 }
 
 
 /**
  * Add the given peer to the blacklist (for the given transport).
- * 
+ *
  * @param peer peer to blacklist
  * @param transport_name transport to blacklist for this peer, NULL for all
  */
 void
 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
-                       const char *transport_name)
+                        const char *transport_name)
 {
-#if DEBUG_TRANSPORT
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Adding peer `%s' with plugin `%s' to blacklist\n",
-             GNUNET_i2s (peer),
-             transport_name);
-#endif
+  char * transport = NULL;
+
+  if (NULL != transport_name)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "Adding peer `%s' with plugin `%s' to blacklist\n",
+               GNUNET_i2s (peer), transport_name);
+    transport = GNUNET_strdup (transport_name);
+  }
+  else
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "Adding peer `%s' with all plugins to blacklist\n",
+               GNUNET_i2s (peer));
   if (blacklist == NULL)
-    blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);
-  GNUNET_CONTAINER_multihashmap_put (blacklist,
-                                    &peer->hashPubKey,
-                                    GNUNET_strdup (transport_name),
-                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    blacklist =
+      GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
+                                           GNUNET_NO);
+
+  GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
+                                     transport,
+                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
 }
 
 
@@ -730,19 +650,40 @@ GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
  * @param cls the transport name to match (const char*)
  * @param key the key (unused)
  * @param value the 'char *' (name of a blacklisted transport)
- * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
+ * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
  */
 static int
 test_blacklisted (void *cls,
-                 const GNUNET_HashCode *key,
+                 const struct GNUNET_PeerIdentity *key,
                  void *value)
 {
   const char *transport_name = cls;
   char *be = value;
 
-  if (0 == strcmp (transport_name,
-                  be))
-    return GNUNET_NO; /* abort iteration! */
+  /* Blacklist entry be:
+   *  (NULL == be): peer is blacklisted with all plugins
+   *  (NULL != be): peer is blacklisted for a specific plugin
+   *
+   * If (NULL != transport_name) we look for a transport specific entry:
+   *  if (transport_name == be) forbidden
+   *
+   */
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
+             GNUNET_i2s (key),
+             (NULL == transport_name) ? "unspecified" : transport_name,
+             (NULL == be) ? "all plugins" : be);
+  /* all plugins for this peer were blacklisted: disallow */
+  if (NULL == value)
+               return GNUNET_NO;
+
+  /* blacklist check for specific transport */
+  if ((NULL != transport_name) && (NULL != value))
+  {
+       if (0 == strcmp (transport_name, be))
+                       return GNUNET_NO;           /* plugin is blacklisted! */
+  }
   return GNUNET_OK;
 }
 
@@ -754,51 +695,90 @@ test_blacklisted (void *cls,
  * @param transport_name name of the transport to test, never NULL
  * @param cont function to call with result
  * @param cont_cls closure for 'cont'
+ * @return handle to the blacklist check, NULL if the decision
+ *        was made instantly and 'cont' was already called
  */
-void
+struct GST_BlacklistCheck *
 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
-                           const char *transport_name,
-                           GST_BlacklistTestContinuation cont,
-                           void *cont_cls)
+                            const char *transport_name,
+                            GST_BlacklistTestContinuation cont, void *cont_cls)
 {
-  struct BlacklistCheck *bc;
-  
-  if ( (blacklist != NULL) &&
-       (GNUNET_SYSERR ==
-       GNUNET_CONTAINER_multihashmap_get_multiple (blacklist,
-                                                   &peer->hashPubKey,
-                                                   &test_blacklisted,
-                                                   (void*) transport_name)) )
-    {
-      /* disallowed by config, disapprove instantly */
-      GNUNET_STATISTICS_update (GST_stats,
-                                gettext_noop ("# disconnects due to blacklist"),
-                                1,
-                                GNUNET_NO);
-      if (cont != NULL)
-       cont (cont_cls, peer, GNUNET_NO);
-      return;
-    }
+  struct GST_BlacklistCheck *bc;
+
+  GNUNET_assert (peer != NULL);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist check for peer `%s':%s\n",
+               GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
+
+  /* Check local blacklist by iterating over hashmap
+   * If iteration is aborted, we found a matching blacklist entry */
+  if ((blacklist != NULL) &&
+      (GNUNET_SYSERR ==
+       GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
+                                                   &test_blacklisted,
+                                                   (void *) transport_name)))
+  {
+    /* Disallowed by config, disapprove instantly */
+    GNUNET_STATISTICS_update (GST_stats,
+                              gettext_noop ("# disconnects due to blacklist"),
+                              1, GNUNET_NO);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disallowing connection to peer `%s' on transport %s\n",
+               GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
+    if (cont != NULL)
+      cont (cont_cls, peer, GNUNET_NO);
+    return NULL;
+  }
 
   if (bl_head == NULL)
-    {
-      /* no blacklist clients, approve instantly */
-      if (cont != NULL)
-        cont (cont_cls, peer, GNUNET_OK);
-      return;
-    }
+  {
+    /* no blacklist clients, approve instantly */
+    if (cont != NULL)
+      cont (cont_cls, peer, GNUNET_OK);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Allowing connection to peer `%s' %s\n",
+               GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "");
+    return NULL;
+  }
 
   /* need to query blacklist clients */
-  bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
+  bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
   bc->peer = *peer;
   bc->cont = cont;
   bc->cont_cls = cont_cls;
   bc->bl_pos = bl_head;
-  bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
-                                      bc);
+  bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
+  return bc;
+}
+
+
+/**
+ * Cancel a blacklist check.
+ *
+ * @param bc check to cancel
+ */
+void
+GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
+{
+  GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
+  if (bc->bl_pos != NULL)
+  {
+    if (bc->bl_pos->bc == bc)
+    {
+      /* we're at the head of the queue, remove us! */
+      bc->bl_pos->bc = NULL;
+    }
+  }
+  if (GNUNET_SCHEDULER_NO_TASK != bc->task)
+  {
+    GNUNET_SCHEDULER_cancel (bc->task);
+    bc->task = GNUNET_SCHEDULER_NO_TASK;
+  }
+  if (NULL != bc->th)
+  {
+    GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
+    bc->th = NULL;
+  }
+  GNUNET_free (bc);
 }
-                                                
 
 
 /* end of file gnunet-service-transport_blacklist.c */