-changing exit helper code to automatically do the network configuration for an exit...
[oweals/gnunet.git] / src / transport / gnunet-service-transport_blacklist.c
index 10eda72b0b1d1effdd9e22d8b6a437b9df2b09f2..105f1fcaf8bf73de058b67543cc9d0e4ef5f3cd6 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet.
 /*
      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
 
      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
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
 
 /**
  * @file transport/gnunet-service-transport_blacklist.c
 
 /**
  * @file transport/gnunet-service-transport_blacklist.c
- * @brief low-level P2P messaging
+ * @brief blacklisting implementation
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @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_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 client registered to perform
+ * blacklisting.
+ */
+struct Blacklisters
+{
+  /**
+   * This is a linked list.
+   */
+  struct Blacklisters *next;
+
+  /**
+   * This is a linked list.
+   */
+  struct Blacklisters *prev;
+
+  /**
+   * Client responsible for this entry.
+   */
+  struct GNUNET_SERVER_Client *client;
+
+  /**
+   * 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.
+   */
+  int waiting_for_reply;
+
+};
+
+
 
 /**
 
 /**
- * Handle a request to start a blacklist.
+ * Context we use when performing a blacklist check.
+ */
+struct GST_BlacklistCheck
+{
+
+  /**
+   * This is a linked list.
+   */
+  struct GST_BlacklistCheck *next;
+
+  /**
+   * This is a linked list.
+   */
+  struct GST_BlacklistCheck *prev;
+
+  /**
+   * Peer being checked.
+   */
+  struct GNUNET_PeerIdentity peer;
+
+  /**
+   * 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 Blacklisters *bl_pos;
+
+  /**
+   * Current task performing the check.
+   */
+  GNUNET_SCHEDULER_TaskIdentifier task;
+
+};
+
+
+/**
+ * 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;
+
+
+/**
+ * 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 (always NULL)
+ * @param cls closure (unused)
  * @param client identification of the client
  * @param client identification of the client
- * @param message the actual message
+ */
+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_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
  */
 void
-GNUNET_TRANSPORT_handle_blacklist_init (void *cls,
-                                       struct GNUNET_SERVER_Client *client,
-                                       const struct GNUNET_MessageHeader *message)
+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)
+{
+  char *be = value;
+
+  GNUNET_free (be);
+  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;
+  }
+}
+
+
+/**
+ * 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 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);
+  bl->waiting_for_reply = GNUNET_YES;
+  return sizeof (bm);
+}
+
+
+/**
+ * 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)
+{
+  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) || (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);
+}
+
+
+/**
+ * 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 ats performance data
+ * @param ats_count number of entries in ats (excluding 0-termination)
+ * @param address the address
+ */
+static void
+test_connection_ok (void *cls, const struct GNUNET_PeerIdentity *neighbour,
+                    const struct GNUNET_ATS_Information *ats,
+                    uint32_t ats_count,
+                    const struct GNUNET_HELLO_Address *address)
+{
+  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
+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);
   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
  */
 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)
 {
 {
+  const struct BlacklistMessage *msg =
+      (const struct BlacklistMessage *) message;
   struct Blacklisters *bl;
   struct Blacklisters *bl;
-  const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
+  struct GST_BlacklistCheck *bc;
 
   bl = bl_head;
 
   bl = bl_head;
-  while ( (bl != NULL) &&
-         (bl->client != client) )
+  while ((bl != NULL) && (bl->client != client))
     bl = bl->next;
   if (bl == NULL)
     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;
+  }
+  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_SERVER_client_done (client, GNUNET_SYSERR);
-      return;
+#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);
     }
     }
-  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);
+  }
+  /* check if any other bc's are waiting for this blacklister */
+  bc = bc_head;
+  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;
     }
     }
-  /* FIXME: trigger continuation... */
 }
 
 
 /**
 }
 
 
 /**
- * Notify the given client about all entries in the blacklist.
+ * Add the given peer to the blacklist (for the given transport).
  *
  *
- * @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)
+ * @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)
-{
-  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;
+void
+GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
+                        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
+  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)
-{
-  GNUNET_SERVER_notification_context_add (blacklist_notifiers, client);
-  GNUNET_CONTAINER_multihashmap_iterate (blacklist,
-                                        &notify_blacklist_entry,
-                                        client);
+static int
+test_blacklisted (void *cls, const GNUNET_HashCode * key, void *value)
+{
+  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'
+ * @return handle to the blacklist check, NULL if the decision
+ *        was made instantly and 'cont' was already called
  */
  */
-int
-GNUNET_TRANSPORT_blacklist_check (const struct GNUNET_PeerIdentity *id)
+struct GST_BlacklistCheck *
+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 GST_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 NULL;
+  }
+
+  if (bl_head == NULL)
+  {
+    /* no blacklist clients, approve instantly */
+    if (cont != NULL)
+      cont (cont_cls, peer, GNUNET_OK);
+    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;
 }
 
 
 /**
 }
 
 
 /**
- * Initialize the blacklisting subsystem.
+ * Cancel a blacklist check.
  *
  *
- * @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);
+ * @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_CONNECTION_notify_transmit_ready_cancel (bc->th);
+    bc->th = NULL;
+  }
+  GNUNET_free (bc);
 }
 
 
 }
 
 
-/* end of gnunet-service-transport_blacklist.c */
+/* end of file gnunet-service-transport_blacklist.c */