-doxygen and minor style fixes
[oweals/gnunet.git] / src / transport / gnunet-service-transport_blacklist.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/gnunet-service-transport_blacklist.c
23  * @brief blacklisting implementation
24  * @author Christian Grothoff
25  * @author Matthias Wachs
26  * @details This is the blacklisting component of transport service. With
27  * blacklisting it is possible to deny connections to specific peers of
28  * to use a specific plugin to a specific peer. Peers can be blacklisted using
29  * the configuration or a blacklist client can be asked.
30  *
31  * To blacklist peers using the configuration you have to add a section to your
32  * configuration containing the peer id of the peer to blacklist and the plugin
33  * if required.
34  *
35  * Example:
36  * To blacklist connections to P565... on peer AG2P... using tcp add:
37  * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
38  * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G = tcp
39  *
40  * To blacklist connections to P565... on peer AG2P... using all plugins add:
41  * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
42  * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G =
43  *
44  * You can also add a blacklist client usign the blacklist api. On a blacklist
45  * check, blacklisting first checks internally if the peer is blacklisted and
46  * if not, it asks the blacklisting clients. Clients are asked if it is OK to
47  * connect to a peer ID, the plugin is omitted.
48  *
49  * On blacklist check for (peer, plugin)
50  * - Do we have a local blacklist entry for this peer and this plugin?
51  *   - YES: disallow connection
52  * - Do we have a local blacklist entry for this peer and all plugins?
53  *   - YES: disallow connection
54  * - Does one of the clients disallow?
55  *   - YES: disallow connection
56  *
57  */
58 #include "platform.h"
59 #include "gnunet-service-transport.h"
60 #include "gnunet-service-transport_blacklist.h"
61 #include "gnunet-service-transport_neighbours.h"
62 #include "transport.h"
63
64 /**
65  * Size of the blacklist hash map.
66  */
67 #define TRANSPORT_BLACKLIST_HT_SIZE 64
68
69
70 /**
71  * Context we use when performing a blacklist check.
72  */
73 struct GST_BlacklistCheck;
74
75
76 /**
77  * Information kept for each client registered to perform
78  * blacklisting.
79  */
80 struct Blacklisters
81 {
82   /**
83    * This is a linked list.
84    */
85   struct Blacklisters *next;
86
87   /**
88    * This is a linked list.
89    */
90   struct Blacklisters *prev;
91
92   /**
93    * Client responsible for this entry.
94    */
95   struct GNUNET_SERVER_Client *client;
96
97   /**
98    * Blacklist check that we're currently performing (or NULL
99    * if we're performing one that has been cancelled).
100    */
101   struct GST_BlacklistCheck *bc;
102
103   /**
104    * Set to #GNUNET_YES if we're currently waiting for a reply.
105    */
106   int waiting_for_reply;
107
108   /**
109    * #GNUNET_YES if we have to call receive_done for this client
110    */
111   int call_receive_done;
112
113 };
114
115
116
117 /**
118  * Context we use when performing a blacklist check.
119  */
120 struct GST_BlacklistCheck
121 {
122
123   /**
124    * This is a linked list.
125    */
126   struct GST_BlacklistCheck *next;
127
128   /**
129    * This is a linked list.
130    */
131   struct GST_BlacklistCheck *prev;
132
133   /**
134    * Peer being checked.
135    */
136   struct GNUNET_PeerIdentity peer;
137
138   /**
139    * Continuation to call with the result.
140    */
141   GST_BlacklistTestContinuation cont;
142
143   /**
144    * Closure for @e cont.
145    */
146   void *cont_cls;
147
148   /**
149    * Current transmission request handle for this client, or NULL if no
150    * request is pending.
151    */
152   struct GNUNET_SERVER_TransmitHandle *th;
153
154   /**
155    * Our current position in the blacklisters list.
156    */
157   struct Blacklisters *bl_pos;
158
159   /**
160    * Current task performing the check.
161    */
162   GNUNET_SCHEDULER_TaskIdentifier task;
163
164 };
165
166
167 /**
168  * Head of DLL of active blacklisting queries.
169  */
170 static struct GST_BlacklistCheck *bc_head;
171
172 /**
173  * Tail of DLL of active blacklisting queries.
174  */
175 static struct GST_BlacklistCheck *bc_tail;
176
177 /**
178  * Head of DLL of blacklisting clients.
179  */
180 static struct Blacklisters *bl_head;
181
182 /**
183  * Tail of DLL of blacklisting clients.
184  */
185 static struct Blacklisters *bl_tail;
186
187 /**
188  * Hashmap of blacklisted peers.  Values are of type 'char *' (transport names),
189  * can be NULL if we have no static blacklist.
190  */
191 static struct GNUNET_CONTAINER_MultiPeerMap *blacklist;
192
193
194 /**
195  * Perform next action in the blacklist check.
196  *
197  * @param cls the `struct BlacklistCheck*`
198  * @param tc unused
199  */
200 static void
201 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
202
203
204 /**
205  * Called whenever a client is disconnected.  Frees our
206  * resources associated with that client.
207  *
208  * @param cls closure (unused)
209  * @param client identification of the client
210  */
211 static void
212 client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client)
213 {
214   struct Blacklisters *bl;
215   struct GST_BlacklistCheck *bc;
216
217   if (NULL == client)
218     return;
219   for (bl = bl_head; bl != NULL; bl = bl->next)
220   {
221     if (bl->client != client)
222       continue;
223     for (bc = bc_head; bc != NULL; bc = bc->next)
224     {
225       if (bc->bl_pos != bl)
226         continue;
227       bc->bl_pos = bl->next;
228       if (bc->th != NULL)
229       {
230         GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
231         bc->th = NULL;
232       }
233       if (bc->task == GNUNET_SCHEDULER_NO_TASK)
234         bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
235     }
236     GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
237     GNUNET_SERVER_client_drop (bl->client);
238     GNUNET_free (bl);
239     break;
240   }
241 }
242
243
244 /**
245  * Function to iterate over options in the blacklisting section for a peer.
246  *
247  * @param cls closure
248  * @param section name of the section
249  * @param option name of the option
250  * @param value value of the option
251  */
252 static void
253 blacklist_cfg_iter (void *cls,
254                     const char *section,
255                     const char *option,
256                     const char *value)
257 {
258   unsigned int *res = cls;
259   struct GNUNET_PeerIdentity peer;
260   char *plugs;
261   char *pos;
262
263   if (GNUNET_OK !=
264       GNUNET_CRYPTO_eddsa_public_key_from_string (option,
265                                                   strlen (option),
266                                                   &peer.public_key))
267     return;
268
269   if ((NULL == value) || (0 == strcmp(value, "")))
270   {
271     /* Blacklist whole peer */
272     GST_blacklist_add_peer (&peer, NULL);
273     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
274                 _("Adding blacklisting entry for peer `%s'\n"),
275                 GNUNET_i2s (&peer));
276   }
277   else
278   {
279     plugs = GNUNET_strdup (value);
280     for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
281       {
282         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
283                     _("Adding blacklisting entry for peer `%s':`%s'\n"),
284                     GNUNET_i2s (&peer), pos);
285         GST_blacklist_add_peer (&peer, pos);
286       }
287     GNUNET_free (plugs);
288   }
289   (*res)++;
290 }
291
292
293 /**
294  * Read blacklist configuration
295  *
296  * @param cfg the configuration handle
297  * @param my_id my peer identity
298  */
299 static void
300 read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
301                               const struct GNUNET_PeerIdentity *my_id)
302 {
303   char cfg_sect[512];
304   unsigned int res = 0;
305
306   GNUNET_snprintf (cfg_sect,
307                    sizeof (cfg_sect),
308                    "transport-blacklist-%s",
309                    GNUNET_i2s_full (my_id));
310   GNUNET_CONFIGURATION_iterate_section_values (cfg,
311                                                cfg_sect,
312                                                &blacklist_cfg_iter,
313                                                &res);
314   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315               "Loaded %u blacklisting entries from configuration\n",
316               res);
317 }
318
319
320 /**
321  * Start blacklist subsystem.
322  *
323  * @param server server used to accept clients from
324  * @param cfg configuration handle
325  * @param my_id my peer id
326  */
327 void
328 GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
329                      const struct GNUNET_CONFIGURATION_Handle *cfg,
330                      const struct GNUNET_PeerIdentity *my_id)
331 {
332   GNUNET_assert (NULL != cfg);
333   GNUNET_assert (NULL != my_id);
334   read_blacklist_configuration (cfg, my_id);
335   GNUNET_SERVER_disconnect_notify (server,
336                                    &client_disconnect_notification,
337                                    NULL);
338 }
339
340
341 /**
342  * Free the given entry in the blacklist.
343  *
344  * @param cls unused
345  * @param key host identity (unused)
346  * @param value the blacklist entry
347  * @return #GNUNET_OK (continue to iterate)
348  */
349 static int
350 free_blacklist_entry (void *cls,
351                       const struct GNUNET_PeerIdentity *key,
352                       void *value)
353 {
354   char *be = value;
355
356   GNUNET_free_non_null (be);
357   return GNUNET_OK;
358 }
359
360
361 /**
362  * Stop blacklist subsystem.
363  */
364 void
365 GST_blacklist_stop ()
366 {
367   if (NULL == blacklist)
368     return;
369   GNUNET_CONTAINER_multipeermap_iterate (blacklist,
370                                          &free_blacklist_entry,
371                                          NULL);
372   GNUNET_CONTAINER_multipeermap_destroy (blacklist);
373   blacklist = NULL;
374 }
375
376
377 /**
378  * Transmit blacklist query to the client.
379  *
380  * @param cls the `struct GST_BlacklistCheck`
381  * @param size number of bytes allowed
382  * @param buf where to copy the message
383  * @return number of bytes copied to @a buf
384  */
385 static size_t
386 transmit_blacklist_message (void *cls, size_t size, void *buf)
387 {
388   struct GST_BlacklistCheck *bc = cls;
389   struct Blacklisters *bl;
390   struct BlacklistMessage bm;
391
392   bc->th = NULL;
393   if (size == 0)
394   {
395     GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
396     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
397     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
398                 "Failed to send blacklist test for peer `%s' to client\n",
399                 GNUNET_i2s (&bc->peer));
400     return 0;
401   }
402   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403               "Sending blacklist test for peer `%s' to client %p\n",
404               GNUNET_i2s (&bc->peer), bc->bl_pos->client);
405   bl = bc->bl_pos;
406   bm.header.size = htons (sizeof (struct BlacklistMessage));
407   bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
408   bm.is_allowed = htonl (0);
409   bm.peer = bc->peer;
410   memcpy (buf, &bm, sizeof (bm));
411   if (GNUNET_YES == bl->call_receive_done)
412   {
413     GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
414     bl->call_receive_done = GNUNET_NO;
415   }
416
417   bl->waiting_for_reply = GNUNET_YES;
418   return sizeof (bm);
419 }
420
421
422 /**
423  * Perform next action in the blacklist check.
424  *
425  * @param cls the `struct GST_BlacklistCheck *`
426  * @param tc unused
427  */
428 static void
429 do_blacklist_check (void *cls,
430                     const struct GNUNET_SCHEDULER_TaskContext *tc)
431 {
432   struct GST_BlacklistCheck *bc = cls;
433   struct Blacklisters *bl;
434
435   bc->task = GNUNET_SCHEDULER_NO_TASK;
436   bl = bc->bl_pos;
437   if (bl == NULL)
438   {
439     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440                 "No other blacklist clients active, will allow neighbour `%s'\n",
441                 GNUNET_i2s (&bc->peer));
442
443     bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK);
444     GNUNET_CONTAINER_DLL_remove(bc_head, bc_tail, bc);
445     GNUNET_free (bc);
446     return;
447   }
448   if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO))
449     return;                     /* someone else busy with this client */
450   bl->bc = bc;
451   bc->th =
452       GNUNET_SERVER_notify_transmit_ready (bl->client,
453                                            sizeof (struct BlacklistMessage),
454                                            GNUNET_TIME_UNIT_FOREVER_REL,
455                                            &transmit_blacklist_message, bc);
456 }
457
458
459 /**
460  * Got the result about an existing connection from a new blacklister.
461  * Shutdown the neighbour if necessary.
462  *
463  * @param cls unused
464  * @param peer the neighbour that was investigated
465  * @param allowed #GNUNET_OK if we can keep it,
466  *                #GNUNET_NO if we must shutdown the connection
467  */
468 static void
469 confirm_or_drop_neighbour (void *cls,
470                            const struct GNUNET_PeerIdentity *peer,
471                            int allowed)
472 {
473   if (GNUNET_OK == allowed)
474     return;                     /* we're done */
475   GNUNET_STATISTICS_update (GST_stats,
476                             gettext_noop ("# disconnects due to blacklist"), 1,
477                             GNUNET_NO);
478   GST_neighbours_force_disconnect (peer);
479 }
480
481
482 /**
483  * Closure for 'test_connection_ok'.
484  */
485 struct TestConnectionContext
486 {
487   /**
488    * Is this the first neighbour we're checking?
489    */
490   int first;
491
492   /**
493    * Handle to the blacklisting client we need to ask.
494    */
495   struct Blacklisters *bl;
496 };
497
498
499 /**
500  * Test if an existing connection is still acceptable given a new
501  * blacklisting client.
502  *
503  * @param cls the `struct TestConnectionContext *`
504  * @param peer neighbour's identity
505  * @param address the address
506  * @param state current state this peer is in
507  * @param state_timeout timeout for the current state of the peer
508  * @param bandwidth_in bandwidth assigned inbound
509  * @param bandwidth_out bandwidth assigned outbound
510  */
511 static void
512 test_connection_ok (void *cls, const struct GNUNET_PeerIdentity *peer,
513     const struct GNUNET_HELLO_Address *address,
514     enum GNUNET_TRANSPORT_PeerState state,
515     struct GNUNET_TIME_Absolute state_timeout,
516     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
517     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
518 {
519   struct TestConnectionContext *tcc = cls;
520   struct GST_BlacklistCheck *bc;
521
522   bc = GNUNET_new (struct GST_BlacklistCheck);
523   GNUNET_CONTAINER_DLL_insert(bc_head, bc_tail, bc);
524   bc->peer = *peer;
525   bc->cont = &confirm_or_drop_neighbour;
526   bc->cont_cls = NULL;
527   bc->bl_pos = tcc->bl;
528   if (GNUNET_YES == tcc->first)
529   {
530     /* all would wait for the same client, no need to
531      * create more than just the first task right now */
532     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
533     tcc->first = GNUNET_NO;
534   }
535 }
536
537
538 /**
539  * Initialize a blacklisting client.  We got a blacklist-init
540  * message from this client, add him to the list of clients
541  * to query for blacklisting.
542  *
543  * @param cls unused
544  * @param client the client
545  * @param message the blacklist-init message that was sent
546  */
547 void
548 GST_blacklist_handle_init (void *cls, struct GNUNET_SERVER_Client *client,
549                            const struct GNUNET_MessageHeader *message)
550 {
551   struct Blacklisters *bl;
552   struct TestConnectionContext tcc;
553
554   for (bl = bl_head; NULL != bl; bl = bl->next)
555     if (bl->client == client)
556     {
557       GNUNET_break (0);
558       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
559       return;
560     }
561
562   GNUNET_SERVER_client_mark_monitor (client);
563   bl = GNUNET_new (struct Blacklisters);
564   bl->client = client;
565   bl->call_receive_done = GNUNET_YES;
566   GNUNET_SERVER_client_keep (client);
567   GNUNET_CONTAINER_DLL_insert_after (bl_head,
568                                      bl_tail,
569                                      bl_tail,
570                                      bl);
571   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
572               "New blacklist client %p\n",
573               client);
574
575   /* confirm that all existing connections are OK! */
576   tcc.bl = bl;
577   tcc.first = GNUNET_YES;
578   GST_neighbours_iterate (&test_connection_ok, &tcc);
579 }
580
581
582 /**
583  * A blacklisting client has sent us reply. Process it.
584  *
585  * @param cls unused
586  * @param client the client
587  * @param message the blacklist-init message that was sent
588  */
589 void
590 GST_blacklist_handle_reply (void *cls, struct GNUNET_SERVER_Client *client,
591                             const struct GNUNET_MessageHeader *message)
592 {
593   const struct BlacklistMessage *msg =
594       (const struct BlacklistMessage *) message;
595   struct Blacklisters *bl;
596   struct GST_BlacklistCheck *bc;
597
598   bl = bl_head;
599   while ((bl != NULL) && (bl->client != client))
600     bl = bl->next;
601   if (NULL == bl)
602   {
603     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604                 "Blacklist client disconnected\n");
605     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
606     return;
607   }
608
609   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610               "Blacklist client %p sent reply for `%s'\n",
611               client,
612               GNUNET_i2s(&msg->peer));
613
614   bc = bl->bc;
615   bl->bc = NULL;
616   bl->waiting_for_reply = GNUNET_NO;
617   bl->call_receive_done = GNUNET_YES; /* Remember to call receive_done */
618   if (NULL != bc)
619   {
620     /* only run this if the blacklist check has not been
621      * cancelled in the meantime... */
622     if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
623     {
624       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625                   "Blacklist check failed, peer not allowed\n");
626       bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
627       GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
628       GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
629       bl->call_receive_done = GNUNET_NO;
630       GNUNET_free (bc);
631       return;
632     }
633     else
634     {
635       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
636                   "Blacklist check succeeded, continuing with checks\n");
637       GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
638       bl->call_receive_done = GNUNET_NO;
639       bc->bl_pos = bc->bl_pos->next;
640       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
641     }
642   }
643   /* check if any other blacklist checks are waiting for this blacklister */
644   for (bc = bc_head; bc != NULL; bc = bc->next)
645     if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
646     {
647       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
648       break;
649     }
650 }
651
652
653 /**
654  * Add the given peer to the blacklist (for the given transport).
655  *
656  * @param peer peer to blacklist
657  * @param transport_name transport to blacklist for this peer, NULL for all
658  */
659 void
660 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
661                         const char *transport_name)
662 {
663   char *transport = NULL;
664
665   if (NULL != transport_name)
666   {
667     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
668                 "Adding peer `%s' with plugin `%s' to blacklist\n",
669                 GNUNET_i2s (peer), transport_name);
670     transport = GNUNET_strdup (transport_name);
671   }
672   else
673     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
674                 "Adding peer `%s' with all plugins to blacklist\n",
675                 GNUNET_i2s (peer));
676   if (NULL == blacklist)
677     blacklist =
678       GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
679                                             GNUNET_NO);
680
681   GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
682                                      transport,
683                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
684 }
685
686
687 /**
688  * Test if the given blacklist entry matches.  If so,
689  * abort the iteration.
690  *
691  * @param cls the transport name to match (const char*)
692  * @param key the key (unused)
693  * @param value the 'char *' (name of a blacklisted transport)
694  * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
695  */
696 static int
697 test_blacklisted (void *cls,
698                   const struct GNUNET_PeerIdentity *key,
699                   void *value)
700 {
701   const char *transport_name = cls;
702   char *be = value;
703
704   /* Blacklist entry be:
705    *  (NULL == be): peer is blacklisted with all plugins
706    *  (NULL != be): peer is blacklisted for a specific plugin
707    *
708    * If (NULL != transport_name) we look for a transport specific entry:
709    *  if (transport_name == be) forbidden
710    *
711    */
712
713   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
714               "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
715               GNUNET_i2s (key),
716               (NULL == transport_name) ? "unspecified" : transport_name,
717               (NULL == be) ? "all plugins" : be);
718   /* all plugins for this peer were blacklisted: disallow */
719   if (NULL == value)
720                 return GNUNET_NO;
721
722   /* blacklist check for specific transport */
723   if ((NULL != transport_name) && (NULL != value))
724   {
725         if (0 == strcmp (transport_name, be))
726                         return GNUNET_NO;           /* plugin is blacklisted! */
727   }
728   return GNUNET_OK;
729 }
730
731
732 /**
733  * Test if a peer/transport combination is blacklisted.
734  *
735  * @param peer the identity of the peer to test
736  * @param transport_name name of the transport to test, never NULL
737  * @param cont function to call with result
738  * @param cont_cls closure for @a cont
739  * @return handle to the blacklist check, NULL if the decision
740  *        was made instantly and @a cont was already called
741  */
742 struct GST_BlacklistCheck *
743 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
744                             const char *transport_name,
745                             GST_BlacklistTestContinuation cont,
746                             void *cont_cls)
747 {
748   struct GST_BlacklistCheck *bc;
749
750   GNUNET_assert (NULL != peer);
751   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
752               "Blacklist check for peer `%s':%s\n",
753               GNUNET_i2s (peer),
754               (NULL != transport_name) ? transport_name : "unspecified");
755
756   /* Check local blacklist by iterating over hashmap
757    * If iteration is aborted, we found a matching blacklist entry */
758   if ((NULL != blacklist) &&
759       (GNUNET_SYSERR ==
760        GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
761                                                    &test_blacklisted,
762                                                    (void *) transport_name)))
763   {
764     /* Disallowed by config, disapprove instantly */
765     GNUNET_STATISTICS_update (GST_stats,
766                               gettext_noop ("# disconnects due to blacklist"),
767                               1, GNUNET_NO);
768     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
769                 _("Disallowing connection to peer `%s' on transport %s\n"),
770                 GNUNET_i2s (peer),
771                 (NULL != transport_name) ? transport_name : "unspecified");
772     if (cont != NULL)
773       cont (cont_cls, peer, GNUNET_NO);
774     return NULL;
775   }
776
777   if (NULL == bl_head)
778   {
779     /* no blacklist clients, approve instantly */
780     if (cont != NULL)
781       cont (cont_cls, peer, GNUNET_OK);
782     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
783                 "Allowing connection to peer `%s' %s\n",
784                 GNUNET_i2s (peer),
785                 (NULL != transport_name) ? transport_name : "");
786     return NULL;
787   }
788
789   /* need to query blacklist clients */
790   bc = GNUNET_new (struct GST_BlacklistCheck);
791   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
792   bc->peer = *peer;
793   bc->cont = cont;
794   bc->cont_cls = cont_cls;
795   bc->bl_pos = bl_head;
796   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
797   return bc;
798 }
799
800
801 /**
802  * Cancel a blacklist check.
803  *
804  * @param bc check to cancel
805  */
806 void
807 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
808 {
809   GNUNET_CONTAINER_DLL_remove (bc_head,
810                                bc_tail,
811                                bc);
812   if (NULL != bc->bl_pos)
813   {
814     if (bc->bl_pos->bc == bc)
815     {
816       /* we're at the head of the queue, remove us! */
817       bc->bl_pos->bc = NULL;
818     }
819   }
820   if (GNUNET_SCHEDULER_NO_TASK != bc->task)
821   {
822     GNUNET_SCHEDULER_cancel (bc->task);
823     bc->task = GNUNET_SCHEDULER_NO_TASK;
824   }
825   if (NULL != bc->th)
826   {
827     GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
828     bc->th = NULL;
829   }
830   GNUNET_free (bc);
831 }
832
833
834 /* end of file gnunet-service-transport_blacklist.c */