stuff
[oweals/gnunet.git] / src / transport / gnunet-service-transport_blacklist.c
index 08ed2c9d2174e842fd40d4c70296c317068dcde9..09aa43452088287617629a3d2e5fd781d172e8d1 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
+ * @brief blacklisting implementation
  * @author Christian Grothoff
  */
 #include "platform.h"
-#include "gnunet_protocols.h"
-#include "gnunet_util_lib.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"
 
 
 /**
- * Information kept for each blacklisted peer.
+ * Size of the blacklist hash map.
  */
-struct BlacklistEntry
-{
-  /**
-   * Identity of the peer being blacklisted by this entry.
-   * (also equivalent to the key)  
-   */
-  struct GNUNET_PeerIdentity peer;
+#define TRANSPORT_BLACKLIST_HT_SIZE 64
 
-  /**
-   * Client responsible for this entry.
-   */
-  struct GNUNET_SERVER_Client *client;
 
-};
+/**
+ * Context we use when performing a blacklist check.
+ */
+struct BlacklistCheck;
 
 
 /**
@@ -71,37 +63,74 @@ struct Blacklisters
    */
   struct GNUNET_SERVER_Client *client;
 
+  /**
+   * Blacklist check that we're currently performing.
+   */
+  struct BlacklistCheck *bc;
+
 };
 
 
+
 /**
- * State of blacklist check to be performed for each
- * connecting peer.
+ * Context we use when performing a blacklist check.
  */
 struct BlacklistCheck
 {
 
-  
+  /**
+   * This is a linked list.
+   */
+  struct BlacklistCheck *next;
 
   /**
-   * Identity of the peer being checked.
+   * This is a linked list.
+   */
+  struct BlacklistCheck *prev;
+
+  /**
+   * Peer being checked.
    */
   struct GNUNET_PeerIdentity peer;
 
   /**
-   * Clients we still need to ask.
+   * Continuation to call with the result.
+   */
+  GST_BlacklistTestContinuation cont;
+
+  /**
+   * Closure for cont.
+   */
+  void *cont_cls;
+
+  /**
+   * Current transmission request handle for this client, or NULL if no
+   * request is pending.
+   */
+  struct GNUNET_CONNECTION_TransmitHandle *th;
+
+  /**
+   * Our current position in the blacklisters list.
    */
-  struct GNUNET_SERVER_Client *pending;
+  struct Blacklisters *bl_pos;
+
+  /**
+   * Current task performing the check.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier task;
 
 };
 
 
+/**
+ * Head of DLL of active blacklisting queries.
+ */
+static struct BlacklistCheck *bc_head;
 
 /**
- * Map of blacklisted peers (maps from peer identities
- * to 'struct BlacklistEntry*' values).
+ * Tail of DLL of active blacklisting queries.
  */
-static struct GNUNET_CONTAINER_MultiHashMap *blacklist;
+static struct BlacklistCheck *bc_tail;
 
 /**
  * Head of DLL of blacklisting clients.
@@ -113,81 +142,512 @@ static struct Blacklisters *bl_head;
  */
 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;
+
 
 /**
- * Free the entries in the blacklist hash map.
+ * Perform next action in the blacklist check.
  *
- * @param cls closure, unused
- * @param key current key code
- * @param value value in the hash map
- * @return GNUNET_YES (continue to iterate)
+ * @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 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_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);
+      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.
+ *
+ */
+static void
+read_blacklist_file ()
+{
+  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]))
+      {
+        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;
+      }
+      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;
+
+      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);
+}
+
+
+/**
+ * Start blacklist subsystem.
+ *
+ * @param server server used to accept clients from
+ */
+void
+GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
+{
+  read_blacklist_file ();
+  GNUNET_SERVER_disconnect_notify (server,
+                                  &client_disconnect_notification,
+                                  NULL);
+}
+
+
+/**
+ * Free the given entry in the blacklist.
+ *
+ * @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,
                      void *value)
 {
-  struct BlacklistEntry *be = value;
+  char *be = value;
 
   GNUNET_free (be);
-  return GNUNET_YES;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Stop blacklist subsystem.
+ */
+void
+GST_blacklist_stop ()
+{
+  if (NULL != blacklist)
+    {
+      GNUNET_CONTAINER_multihashmap_iterate (blacklist,
+                                            &free_blacklist_entry,
+                                            NULL);
+      GNUNET_CONTAINER_multihashmap_destroy (blacklist);
+      blacklist = NULL;
+    }
 }
 
 
 /**
- * Task run when we are shutting down.  Cleans up.
+ * Transmit blacklist query to the client.
  *
- * @param cls closure (unused)
- * @param tc scheduler context (unused)
+ * @param cls the 'struct BlacklistCheck'
+ * @param size number of bytes allowed
+ * @param buf where to copy the message
+ * @return number of bytes copied to buf
  */
-static void 
-shutdown_task (void *cls,
-              const struct GNUNET_SCHEDULER_TaskContext *tc)
+static size_t
+transmit_blacklist_message (void *cls,
+                           size_t size,
+                           void *buf)
 {
-  GNUNET_CONTAINER_multihashmap_iterate (blacklist,
-                                        &free_blacklist_entry,
-                                        NULL);
-  GNUNET_CONTAINER_multihashmap_destroy (blacklist);
-  blacklist = NULL;
+  struct 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_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Sending blacklist test for peer `%s' to client\n",
+             GNUNET_i2s (&bc->peer));
+#endif
+  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);
+  return sizeof (bm);
 }
 
 
 /**
- * Handle a request to start a blacklist.
+ * Perform next action in the blacklist check.
  *
- * @param cls closure (always NULL)
- * @param client identification of the client
- * @param message the actual message
+ * @param cls the 'struct BlacklistCheck*'
+ * @param tc unused
+ */
+static void
+do_blacklist_check (void *cls,
+                   const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct 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 */
+  bl->bc = bc;
+  bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client,
+                                               sizeof (struct BlacklistMessage),
+                                               GNUNET_TIME_UNIT_FOREVER_REL,
+                                               &transmit_blacklist_message,
+                                               bc);    
+}
+
+
+/**
+ * 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 pid neighbour's identity
+ * @param ats performance data
+ * @param ats_count number of entries in ats (excluding 0-termination)
+ */
+static void
+test_connection_ok (void *cls,
+                   const struct GNUNET_PeerIdentity *neighbour,
+                   const struct GNUNET_TRANSPORT_ATS_Information *ats,
+                   uint32_t ats_count)
+{
+  struct TestConnectionContext *tcc = cls;
+  struct BlacklistCheck *bc;
+
+  bc = GNUNET_malloc (sizeof (struct 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_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)
+       {
+         GNUNET_break (0);
+         GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+         return;
+       }
+      bl = bl->next;
+    }
   bl = GNUNET_malloc (sizeof (struct Blacklisters));
   bl->client = client;
   GNUNET_SERVER_client_keep (client);
-  GNUNET_CONTAINER_DLL_insert (bl_head, bl_tail, bl);
-  /* FIXME: confirm that all existing connections are OK! */
+  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 to blacklist a peer.
+ * A blacklisting client has sent us reply. Process it.
  *
- * @param cls closure (always NULL)
- * @param client identification of the client
- * @param message the actual message
+ * @param cls unused
+ * @param client the client
+ * @param message the blacklist-init message that was sent
  */
 void
-GNUNET_TRANSPORT_handle_blacklist_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)
 {
-  struct Blacklisters *bl;
   const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
+  struct Blacklisters *bl;
+  struct BlacklistCheck *bc;
 
   bl = bl_head;
   while ( (bl != NULL) &&
@@ -195,104 +655,150 @@ GNUNET_TRANSPORT_handle_blacklist_reply (void *cls,
     bl = bl->next;
   if (bl == NULL)
     {
-      GNUNET_SERVER_client_done (client, GNUNET_SYSERR);
+#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;
     }
+  bc = bl->bc;
+  bl->bc = NULL;
   if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
-    {    
-      be = GNUNET_malloc (sizeof (struct BlacklistEntry));
-      be->peer = msg->peer;
-      be->client = client;
-      GNUNET_CONTAINER_multihashmap_put (blacklist,
-                                        &msg->peer.hashPubKey,
-                                        be,
-                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    {
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Blacklist check failed, peer not allowed\n");
+#endif
+      bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
+      GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
+      GNUNET_free (bc);
+    }
+  else
+    {
+#if DEBUG_TRANSPORT
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Blacklist check succeeded, continuing with checks\n");
+#endif
+      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 */
+  bc = bc_head;
+  while (bc != NULL)
+    {
+      if ( (bc->bl_pos == bl) &&
+          (GNUNET_SCHEDULER_NO_TASK == bc->task) )
+       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
+                                            bc);
+      bc = bc->next;
     }
-  /* FIXME: trigger continuation... */
 }
 
 
 /**
- * Notify the given client about all entries in the blacklist.
- *
- * @param cls closure, refers to the 'struct GNUNET_SERVER_Client' to notify
- * @param key current key code (peer identity, not used)
- * @param value value in the hash map, the 'struct BlacklistEntry*'
- * @return GNUNET_YES (continue to iterate)
+ * 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
  */
-static int
-notify_blacklist_entry (void *cls,
-                       const GNUNET_HashCode *key,
-                       void *value)
+void
+GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
+                       const char *transport_name)
 {
-  struct GNUNET_SERVER_Client *client = cls;
-  struct BlacklistEntry *be = value;
-  struct BlacklistMessage msg;
-
-  msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST);
-  msg.header.size = htons (sizeof (struct BlacklistMessage));
-  msg.reserved = htonl (0);
-  msg.peer = be->peer;
-  msg.until = GNUNET_TIME_absolute_hton (be->until);
-  GNUNET_SERVER_notification_context_unicast (blacklist_notifiers,
-                                             client,
-                                             &msg.header,
-                                             GNUNET_NO);
-  return GNUNET_YES;
+#if DEBUG_TRANSPORT
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Adding peer `%s' with plugin `%s' to blacklist\n",
+             GNUNET_i2s (peer),
+             transport_name);
+#endif
+  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);
 }
 
 
 /**
- * Handle a request for notification of blacklist changes.
+ * Test if the given blacklist entry matches.  If so,
+ * abort the iteration.
  *
- * @param cls closure (always NULL)
- * @param client identification of the client
- * @param message the actual message
+ * @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_handle_blacklist_notify (void *cls,
-                                         struct GNUNET_SERVER_Client *client,
-                                         const struct GNUNET_MessageHeader *message)
+static int
+test_blacklisted (void *cls,
+                 const GNUNET_HashCode *key,
+                 void *value)
 {
-  GNUNET_SERVER_notification_context_add (blacklist_notifiers, client);
-  GNUNET_CONTAINER_multihashmap_iterate (blacklist,
-                                        &notify_blacklist_entry,
-                                        client);
+  const char *transport_name = cls;
+  char *be = value;
+
+  if (0 == strcmp (transport_name,
+                  be))
+    return GNUNET_NO; /* abort iteration! */
+  return GNUNET_OK;
 }
 
 
 /**
- * Is the given peer currently blacklisted?
+ * Test if a peer/transport combination is blacklisted.
  *
- * @param id identity of the peer
- * @return GNUNET_YES if the peer is blacklisted, GNUNET_NO if not
+ * @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'
  */
-int
-GNUNET_TRANSPORT_blacklist_check (const struct GNUNET_PeerIdentity *id)
+void
+GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
+                           const char *transport_name,
+                           GST_BlacklistTestContinuation cont,
+                           void *cont_cls)
 {
-  if (GNUNET_CONTAINER_multihashmap_contains (blacklist, &id->hashPubKey))    
-    return GNUNET_YES;
+  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;
+    }
 
+  if (bl_head == NULL)
+    {
+      /* no blacklist clients, approve instantly */
+      if (cont != NULL)
+        cont (cont_cls, peer, GNUNET_OK);
+      return;
+    }
 
-/**
- * Initialize the blacklisting subsystem.
- *
- * @param server server of the transport service
- * @param s scheduler to use
- */
-void 
-GNUNET_TRANSPORT_blacklist_init (struct GNUNET_SERVER_Handle *server,
-                                struct GNUNET_SCHEDULER_Handle *s)
-{
-  sched = s;
-  blacklist = GNUNET_CONTAINER_multihashmap_create (4);
-  GNUNET_SCHEDULER_add_delayed (sched,
-                               GNUNET_TIME_UNIT_FOREVER_REL,
-                               &shutdown_task,
-                               NULL);
+  /* need to query blacklist clients */
+  bc = GNUNET_malloc (sizeof (struct 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);
 }
+                                                
 
 
-/* end of gnunet-service-transport_blacklist.c */
+/* end of file gnunet-service-transport_blacklist.c */