-doxygen
[oweals/gnunet.git] / src / transport / gnunet-service-transport_blacklist.c
index a1b13f33ecc1ce1249a4970bba3cee9d5d3a8e3e..87d7539295ba91874ad8f56eeee1bea177811820 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
-     (C) 2010 Christian Grothoff (and other contributing authors)
+     (C) 2010,2011 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
 
 /**
  * @file transport/gnunet-service-transport_blacklist.c
- * @brief low-level P2P messaging
- * @author Christian Grothoff
+ * @brief blacklisting implementation
+ * @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_protocols.h"
-#include "gnunet_service_lib.h"
-#include "transport.h"
+#include "gnunet-service-transport.h"
 #include "gnunet-service-transport_blacklist.h"
+#include "gnunet-service-transport_neighbours.h"
+#include "transport.h"
+
+/**
+ * Size of the blacklist hash map.
+ */
+#define TRANSPORT_BLACKLIST_HT_SIZE 64
+
+
+/**
+ * Context we use when performing a blacklist check.
+ */
+struct GST_BlacklistCheck;
 
 
 /**
- * Information kept for each blacklisted peer.
+ * Information kept for each client registered to perform
+ * blacklisting.
  */
-struct BlacklistEntry
+struct Blacklisters
 {
   /**
-   * How long until this entry times out?
+   * This is a linked list.
    */
-  struct GNUNET_TIME_Absolute until;
+  struct Blacklisters *next;
 
   /**
-   * Task scheduled to run the moment the time does run out.
+   * This is a linked list.
    */
-  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
-};
-
+  struct Blacklisters *prev;
 
-/**
- * Entry in list of notifications still to transmit to
- * a client.
- */
-struct PendingNotificationList 
-{
+  /**
+   * Client responsible for this entry.
+   */
+  struct GNUNET_SERVER_Client *client;
 
   /**
-   * This is a linked list.
+   * Blacklist check that we're currently performing (or NULL
+   * if we're performing one that has been cancelled).
    */
-  struct PendingNotificationList *next;
+  struct GST_BlacklistCheck *bc;
 
   /**
-   * Identity of the peer to send notification about.
+   * Set to GNUNET_YES if we're currently waiting for a reply.
    */
-  struct GNUNET_PeerIdentity peer;
+  int waiting_for_reply;
 
 };
 
 
+
 /**
- * List of clients to notify whenever the blacklist changes.
+ * Context we use when performing a blacklist check.
  */
-struct BlacklistNotificationList
+struct GST_BlacklistCheck
 {
 
   /**
    * This is a linked list.
    */
-  struct BlacklistNotificationList *next;
+  struct GST_BlacklistCheck *next;
 
   /**
-   * Client to notify.
+   * This is a linked list.
    */
-  struct GNUNET_SERVER_Client *client;
+  struct GST_BlacklistCheck *prev;
+
+  /**
+   * Peer being checked.
+   */
+  struct GNUNET_PeerIdentity peer;
 
   /**
-   * Pending request for transmission to client, or NULL.
-   */ 
-  struct GNUNET_CONNECTION_TransmitHandle *req;
+   * Continuation to call with the result.
+   */
+  GST_BlacklistTestContinuation cont;
+
+  /**
+   * Closure for cont.
+   */
+  void *cont_cls;
 
   /**
-   * Blacklist entries that still need to be submitted.
+   * Current transmission request handle for this client, or NULL if no
+   * request is pending.
    */
-  struct PendingNotificationList *pending;
-  
+  struct GNUNET_SERVER_TransmitHandle *th;
+
+  /**
+   * Our current position in the blacklisters list.
+   */
+  struct Blacklisters *bl_pos;
+
+  /**
+   * Current task performing the check.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier task;
+
 };
 
 
 /**
- * Map of blacklisted peers (maps from peer identities
- * to 'struct BlacklistEntry*' values).
+ * Head of DLL of active blacklisting queries.
+ */
+static struct GST_BlacklistCheck *bc_head;
+
+/**
+ * Tail of DLL of active blacklisting queries.
+ */
+static struct GST_BlacklistCheck *bc_tail;
+
+/**
+ * Head of DLL of blacklisting clients.
+ */
+static struct Blacklisters *bl_head;
+
+/**
+ * Tail of DLL of blacklisting clients.
+ */
+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;
+
+
+/**
+ * Perform next action in the blacklist check.
+ *
+ * @param cls the 'struct BlacklistCheck*'
+ * @param tc unused
+ */
+static void
+do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Called whenever a client is disconnected.  Frees our
+ * resources associated with that client.
+ *
+ * @param cls closure (unused)
+ * @param client identification of the client
+ */
+static void
+client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client)
+{
+  struct Blacklisters *bl;
+  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 (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;
+  }
+}
+
+
+/**
+ * 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
+blacklist_cfg_iter (void *cls, const char *section,
+                   const char *option,
+                   const char *value)
+{
+  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_INFO,
+                   _("Adding blacklisting entry for peer `%s':`%s'\n"),
+                   GNUNET_i2s (&peer), pos);
+       GST_blacklist_add_peer (&peer, pos);
+      }
+    GNUNET_free (plugs);
+  }
+  (*res)++;
+}
+
 
 /**
- * Linked list of clients to notify whenever the blacklist changes.
+ * Read blacklist configuration
+ *
+ * @param cfg the configuration handle
+ * @param my_id my peer identity
  */
-static struct BlacklistNotificationList *blacklist_notifiers;
+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);
+}
+
 
 /**
- * Our scheduler.
+ * Start blacklist subsystem.
+ *
+ * @param server server used to accept clients from
+ * @param cfg configuration handle
+ * @param my_id my peer id
  */
-static struct GNUNET_SCHEDULER_Handle *sched;
+void
+GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
+                    const struct GNUNET_CONFIGURATION_Handle *cfg,
+                    const struct GNUNET_PeerIdentity *my_id)
+{
+  GNUNET_assert (NULL != cfg);
+  GNUNET_assert (NULL != my_id);
+  read_blacklist_configuration (cfg, my_id);
+  GNUNET_SERVER_disconnect_notify (server, &client_disconnect_notification,
+                                   NULL);
+}
 
 
 /**
- * Free the entries in the blacklist hash map.
+ * Free the given entry in the blacklist.
  *
- * @param cls closure, unused
- * @param key current key code
- * @param value value in the hash map
- * @return GNUNET_YES if we should continue to
- *         iterate,
- *         GNUNET_NO if not.
+ * @param cls unused
+ * @param key host identity (unused)
+ * @param value the blacklist entry
+ * @return GNUNET_OK (continue to iterate)
  */
 static int
 free_blacklist_entry (void *cls,
-                     const GNUNET_HashCode *key,
+                     const struct GNUNET_PeerIdentity *key,
                      void *value)
 {
-  struct BlacklistEntry *be = value;
+  char *be = value;
+
+  GNUNET_free_non_null (be);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Stop blacklist subsystem.
+ */
+void
+GST_blacklist_stop ()
+{
+  if (NULL != blacklist)
+  {
+    GNUNET_CONTAINER_multipeermap_iterate (blacklist, &free_blacklist_entry,
+                                           NULL);
+    GNUNET_CONTAINER_multipeermap_destroy (blacklist);
+    blacklist = NULL;
+  }
+}
+
 
-  GNUNET_SCHEDULER_cancel (sched,
-                          be->timeout_task);
-  GNUNET_free (be);
-  return GNUNET_YES;
+/**
+ * Transmit blacklist query to the client.
+ *
+ * @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)
+{
+  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;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "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);
+  bm.is_allowed = htonl (0);
+  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);
 }
 
 
-static void 
-shutdown_task (void *cls,
-              const struct GNUNET_SCHEDULER_TaskContext *tc)
+/**
+ * Perform next action in the blacklist check.
+ *
+ * @param cls the 'struct GST_BlacklistCheck*'
+ * @param tc unused
+ */
+static void
+do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  GNUNET_CONTAINER_multihashmap_iterate (blacklist,
-                                        &free_blacklist_entry,
-                                        NULL);
-  GNUNET_CONTAINER_multihashmap_destroy (blacklist);
+  struct GST_BlacklistCheck *bc = cls;
+  struct Blacklisters *bl;
+
+  bc->task = GNUNET_SCHEDULER_NO_TASK;
+  bl = bc->bl_pos;
+  if (bl == NULL)
+  {
+    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);
 }
 
 
 /**
- * Handle a request to blacklist a peer.
+ * Got the result about an existing connection from a new blacklister.
+ * Shutdown the neighbour if necessary.
+ *
+ * @param cls unused
+ * @param peer the neighbour that was investigated
+ * @param allowed GNUNET_OK if we can keep it,
+ *                GNUNET_NO if we must shutdown the connection
+ */
+static void
+confirm_or_drop_neighbour (void *cls, const struct GNUNET_PeerIdentity *peer,
+                           int allowed)
+{
+  if (GNUNET_OK == allowed)
+    return;                     /* we're done */
+  GNUNET_STATISTICS_update (GST_stats,
+                            gettext_noop ("# disconnects due to blacklist"), 1,
+                            GNUNET_NO);
+  GST_neighbours_force_disconnect (peer);
+}
+
+
+/**
+ * Closure for 'test_connection_ok'.
+ */
+struct TestConnectionContext
+{
+  /**
+   * Is this the first neighbour we're checking?
+   */
+  int first;
+
+  /**
+   * Handle to the blacklisting client we need to ask.
+   */
+  struct Blacklisters *bl;
+};
+
+
+/**
+ * Test if an existing connection is still acceptable given a new
+ * blacklisting client.
+ *
+ * @param cls the 'struct TestConnectionContest'
+ * @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_HELLO_Address *address,
+                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
+                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
+{
+  struct TestConnectionContext *tcc = cls;
+  struct GST_BlacklistCheck *bc;
+
+  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;
+  }
+}
+
+
+/**
+ * Initialize a blacklisting client.  We got a blacklist-init
+ * message from this client, add him to the list of clients
+ * to query for blacklisting.
+ *
+ * @param cls unused
+ * @param client the client
+ * @param message the blacklist-init message that was sent
  */
 void
-GNUNET_TRANSPORT_handle_blacklist (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)
+    {
+      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);
+  GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
+
+  /* confirm that all existing connections are OK! */
+  tcc.bl = bl;
+  tcc.first = GNUNET_YES;
+  GST_neighbours_iterate (&test_connection_ok, &tcc);
 }
 
 
 /**
- * Handle a request for notification of blacklist changes.
+ * A blacklisting client has sent us reply. Process it.
+ *
+ * @param cls unused
+ * @param client the client
+ * @param message the blacklist-init message that was sent
  */
 void
-GNUNET_TRANSPORT_handle_blacklist_notify (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;
+  struct Blacklisters *bl;
+  struct GST_BlacklistCheck *bc;
+
+  bl = bl_head;
+  while ((bl != NULL) && (bl->client != client))
+    bl = bl->next;
+  if (bl == NULL)
+  {
+    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;
+  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)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "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
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Blacklist check succeeded, continuing with checks\n");
+      bc->bl_pos = bc->bl_pos->next;
+      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
+    }
+  }
+  /* check if any other bc's are waiting for this blacklister */
+  for (bc = bc_head; bc != NULL; bc = bc->next)
+    if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
+    {
+      bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
+      break;
+    }
 }
 
 
 /**
- * Is the given peer currently blacklisted?
+ * Add the given peer to the blacklist (for the given transport).
  *
- * @param id identity of the peer
- * @return GNUNET_YES if the peer is blacklisted, GNUNET_NO if not
+ * @param peer peer to blacklist
+ * @param transport_name transport to blacklist for this peer, NULL for all
  */
-int
-GNUNET_TRANSPORT_blacklist_check (const struct GNUNET_PeerIdentity *id)
+void
+GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
+                        const char *transport_name)
 {
-  return GNUNET_CONTAINER_multihashmap_contains (blacklist, &id->hashPubKey);
+  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_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
+                                           GNUNET_NO);
+
+  GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
+                                     transport,
+                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
 }
 
 
 /**
- * Initialize the blacklisting subsystem.
+ * Test if the given blacklist entry matches.  If so,
+ * abort the iteration.
  *
- * @param s scheduler to use
+ * @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
  */
-void 
-GNUNET_TRANSPORT_blacklist_init (struct GNUNET_SCHEDULER_Handle *s)
+static int
+test_blacklisted (void *cls,
+                 const struct GNUNET_PeerIdentity *key,
+                 void *value)
 {
-  sched = s;
-  blacklist = GNUNET_CONTAINER_multihashmap_create (4);
-  GNUNET_SCHEDULER_add_delayed (sched,
-                               GNUNET_TIME_UNIT_FOREVER_REL,
-                               &shutdown_task,
-                               NULL);
+  const char *transport_name = cls;
+  char *be = value;
+
+  /* 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;
+}
+
+
+/**
+ * Test if a peer/transport combination is blacklisted.
+ *
+ * @param peer the identity of the peer to test
+ * @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
+ */
+struct GST_BlacklistCheck *
+GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
+                            const char *transport_name,
+                            GST_BlacklistTestContinuation cont, void *cont_cls)
+{
+  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);
+    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 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);
+  return bc;
 }
 
-/* end of gnunet-service-transport_blacklist.c */
+
+/**
+ * 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 */