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