-fix NPE
[oweals/gnunet.git] / src / transport / gnunet-service-transport_blacklist.c
1 /*
2      This file is part of GNUnet.
3      Copyright (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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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    * Address for #GST_blacklist_abort_matching(), can be NULL.
150    */
151   struct GNUNET_HELLO_Address *address;
152
153   /**
154    * Session for #GST_blacklist_abort_matching(), can be NULL.
155    */
156   struct GNUNET_ATS_Session *session;
157
158   /**
159    * Current transmission request handle for this client, or NULL if no
160    * request is pending.
161    */
162   struct GNUNET_SERVER_TransmitHandle *th;
163
164   /**
165    * Our current position in the blacklisters list.
166    */
167   struct Blacklisters *bl_pos;
168
169   /**
170    * Current task performing the check.
171    */
172   struct GNUNET_SCHEDULER_Task *task;
173
174 };
175
176
177 /**
178  * Head of DLL of active blacklisting queries.
179  */
180 static struct GST_BlacklistCheck *bc_head;
181
182 /**
183  * Tail of DLL of active blacklisting queries.
184  */
185 static struct GST_BlacklistCheck *bc_tail;
186
187 /**
188  * Head of DLL of blacklisting clients.
189  */
190 static struct Blacklisters *bl_head;
191
192 /**
193  * Tail of DLL of blacklisting clients.
194  */
195 static struct Blacklisters *bl_tail;
196
197 /**
198  * Hashmap of blacklisted peers.  Values are of type 'char *' (transport names),
199  * can be NULL if we have no static blacklist.
200  */
201 static struct GNUNET_CONTAINER_MultiPeerMap *blacklist;
202
203
204 /**
205  * Perform next action in the blacklist check.
206  *
207  * @param cls the `struct BlacklistCheck*`
208  * @param tc unused
209  */
210 static void
211 do_blacklist_check (void *cls, 
212                     const struct GNUNET_SCHEDULER_TaskContext *tc);
213
214
215 /**
216  * Called whenever a client is disconnected.  Frees our
217  * resources associated with that client.
218  *
219  * @param cls closure (unused)
220  * @param client identification of the client
221  */
222 static void
223 client_disconnect_notification (void *cls,
224                                 struct GNUNET_SERVER_Client *client)
225 {
226   struct Blacklisters *bl;
227   struct GST_BlacklistCheck *bc;
228
229   if (NULL == client)
230     return;
231   for (bl = bl_head; bl != NULL; bl = bl->next)
232   {
233     if (bl->client != client)
234       continue;
235     for (bc = bc_head; NULL != bc; bc = bc->next)
236     {
237       if (bc->bl_pos != bl)
238         continue;
239       bc->bl_pos = bl->next;
240       if (NULL != bc->th)
241       {
242         GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
243         bc->th = NULL;
244       }
245       if (NULL == bc->task)
246         bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
247     }
248     GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
249     GNUNET_SERVER_client_drop (bl->client);
250     GNUNET_free (bl);
251     break;
252   }
253 }
254
255
256 /**
257  * Function to iterate over options in the blacklisting section for a peer.
258  *
259  * @param cls closure
260  * @param section name of the section
261  * @param option name of the option
262  * @param value value of the option
263  */
264 static void
265 blacklist_cfg_iter (void *cls,
266                     const char *section,
267                     const char *option,
268                     const char *value)
269 {
270   unsigned int *res = cls;
271   struct GNUNET_PeerIdentity peer;
272   char *plugs;
273   char *pos;
274
275   if (GNUNET_OK !=
276       GNUNET_CRYPTO_eddsa_public_key_from_string (option,
277                                                   strlen (option),
278                                                   &peer.public_key))
279     return;
280
281   if ((NULL == value) || (0 == strcmp(value, "")))
282   {
283     /* Blacklist whole peer */
284     GST_blacklist_add_peer (&peer, NULL);
285     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
286                 _("Adding blacklisting entry for peer `%s'\n"),
287                 GNUNET_i2s (&peer));
288   }
289   else
290   {
291     plugs = GNUNET_strdup (value);
292     for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
293       {
294         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
295                     _("Adding blacklisting entry for peer `%s':`%s'\n"),
296                     GNUNET_i2s (&peer), pos);
297         GST_blacklist_add_peer (&peer, pos);
298       }
299     GNUNET_free (plugs);
300   }
301   (*res)++;
302 }
303
304
305 /**
306  * Read blacklist configuration
307  *
308  * @param cfg the configuration handle
309  * @param my_id my peer identity
310  */
311 static void
312 read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
313                               const struct GNUNET_PeerIdentity *my_id)
314 {
315   char cfg_sect[512];
316   unsigned int res = 0;
317
318   GNUNET_snprintf (cfg_sect,
319                    sizeof (cfg_sect),
320                    "transport-blacklist-%s",
321                    GNUNET_i2s_full (my_id));
322   GNUNET_CONFIGURATION_iterate_section_values (cfg,
323                                                cfg_sect,
324                                                &blacklist_cfg_iter,
325                                                &res);
326   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327               "Loaded %u blacklisting entries from configuration\n",
328               res);
329 }
330
331
332 /**
333  * Start blacklist subsystem.
334  *
335  * @param server server used to accept clients from
336  * @param cfg configuration handle
337  * @param my_id my peer id
338  */
339 void
340 GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
341                      const struct GNUNET_CONFIGURATION_Handle *cfg,
342                      const struct GNUNET_PeerIdentity *my_id)
343 {
344   GNUNET_assert (NULL != cfg);
345   GNUNET_assert (NULL != my_id);
346   read_blacklist_configuration (cfg, my_id);
347   GNUNET_SERVER_disconnect_notify (server,
348                                    &client_disconnect_notification,
349                                    NULL);
350 }
351
352
353 /**
354  * Free the given entry in the blacklist.
355  *
356  * @param cls unused
357  * @param key host identity (unused)
358  * @param value the blacklist entry
359  * @return #GNUNET_OK (continue to iterate)
360  */
361 static int
362 free_blacklist_entry (void *cls,
363                       const struct GNUNET_PeerIdentity *key,
364                       void *value)
365 {
366   char *be = value;
367
368   GNUNET_free_non_null (be);
369   return GNUNET_OK;
370 }
371
372
373 /**
374  * Stop blacklist subsystem.
375  */
376 void
377 GST_blacklist_stop ()
378 {
379   if (NULL == blacklist)
380     return;
381   GNUNET_CONTAINER_multipeermap_iterate (blacklist,
382                                          &free_blacklist_entry,
383                                          NULL);
384   GNUNET_CONTAINER_multipeermap_destroy (blacklist);
385   blacklist = NULL;
386 }
387
388
389 /**
390  * Transmit blacklist query to the client.
391  *
392  * @param cls the `struct GST_BlacklistCheck`
393  * @param size number of bytes allowed
394  * @param buf where to copy the message
395  * @return number of bytes copied to @a buf
396  */
397 static size_t
398 transmit_blacklist_message (void *cls, 
399                             size_t size,
400                             void *buf)
401 {
402   struct GST_BlacklistCheck *bc = cls;
403   struct Blacklisters *bl;
404   struct BlacklistMessage bm;
405
406   bc->th = NULL;
407   if (0 == size)
408   {
409     GNUNET_assert (NULL == bc->task);
410     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
411                                          bc);
412     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
413                 "Failed to send blacklist test for peer `%s' to client\n",
414                 GNUNET_i2s (&bc->peer));
415     return 0;
416   }
417   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418               "Sending blacklist test for peer `%s' to client %p\n",
419               GNUNET_i2s (&bc->peer), 
420               bc->bl_pos->client);
421   bl = bc->bl_pos;
422   bm.header.size = htons (sizeof (struct BlacklistMessage));
423   bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
424   bm.is_allowed = htonl (0);
425   bm.peer = bc->peer;
426   memcpy (buf,
427           &bm, 
428           sizeof (bm));
429   if (GNUNET_YES == bl->call_receive_done)
430   {
431     GNUNET_SERVER_receive_done (bl->client,
432                                 GNUNET_OK);
433     bl->call_receive_done = GNUNET_NO;
434   }
435
436   bl->waiting_for_reply = GNUNET_YES;
437   return sizeof (bm);
438 }
439
440
441 /**
442  * Perform next action in the blacklist check.
443  *
444  * @param cls the `struct GST_BlacklistCheck *`
445  * @param tc unused
446  */
447 static void
448 do_blacklist_check (void *cls,
449                     const struct GNUNET_SCHEDULER_TaskContext *tc)
450 {
451   struct GST_BlacklistCheck *bc = cls;
452   struct Blacklisters *bl;
453
454   bc->task = NULL;
455   bl = bc->bl_pos;
456   if (NULL == bl)
457   {
458     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
459                 "No other blacklist clients active, will allow neighbour `%s'\n",
460                 GNUNET_i2s (&bc->peer));
461
462     bc->cont (bc->cont_cls, 
463               &bc->peer,
464               bc->address,
465               bc->session,
466               GNUNET_OK);
467     GST_blacklist_test_cancel (bc);
468     return;
469   }
470   if ( (NULL != bl->bc) ||
471        (GNUNET_NO != bl->waiting_for_reply) )
472     return;                     /* someone else busy with this client */
473   bl->bc = bc;
474   bc->th =
475       GNUNET_SERVER_notify_transmit_ready (bl->client,
476                                            sizeof (struct BlacklistMessage),
477                                            GNUNET_TIME_UNIT_FOREVER_REL,
478                                            &transmit_blacklist_message, 
479                                            bc);
480 }
481
482
483 /**
484  * Got the result about an existing connection from a new blacklister.
485  * Shutdown the neighbour if necessary.
486  *
487  * @param cls unused
488  * @param peer the neighbour that was investigated
489  * @param address address associated with the request
490  * @param session session associated with the request
491  * @param allowed #GNUNET_OK if we can keep it,
492  *                #GNUNET_NO if we must shutdown the connection
493  */
494 static void
495 confirm_or_drop_neighbour (void *cls,
496                            const struct GNUNET_PeerIdentity *peer,
497                            const struct GNUNET_HELLO_Address *address,
498                            struct GNUNET_ATS_Session *session,
499                            int allowed)
500 {
501   if (GNUNET_OK == allowed)
502     return;                     /* we're done */
503   GNUNET_STATISTICS_update (GST_stats,
504                             gettext_noop ("# disconnects due to blacklist"), 
505                             1,
506                             GNUNET_NO);
507   GST_neighbours_force_disconnect (peer);
508 }
509
510
511 /**
512  * Closure for #test_connection_ok().
513  */
514 struct TestConnectionContext
515 {
516   /**
517    * Is this the first neighbour we're checking?
518    */
519   int first;
520
521   /**
522    * Handle to the blacklisting client we need to ask.
523    */
524   struct Blacklisters *bl;
525 };
526
527
528 /**
529  * Test if an existing connection is still acceptable given a new
530  * blacklisting client.
531  *
532  * @param cls the `struct TestConnectionContext *`
533  * @param peer identity of the peer
534  * @param address the address
535  * @param state current state this peer is in
536  * @param state_timeout timeout for the current state of the peer
537  * @param bandwidth_in bandwidth assigned inbound
538  * @param bandwidth_out bandwidth assigned outbound
539  */
540 static void
541 test_connection_ok (void *cls,
542                     const struct GNUNET_PeerIdentity *peer,
543                     const struct GNUNET_HELLO_Address *address,
544                     enum GNUNET_TRANSPORT_PeerState state,
545                     struct GNUNET_TIME_Absolute state_timeout,
546                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
547                     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
548 {
549   struct TestConnectionContext *tcc = cls;
550   struct GST_BlacklistCheck *bc;
551
552   bc = GNUNET_new (struct GST_BlacklistCheck);
553   GNUNET_CONTAINER_DLL_insert (bc_head,
554                                bc_tail,
555                                bc);
556   bc->peer = *peer;
557   bc->address = GNUNET_HELLO_address_copy (address);
558   bc->cont = &confirm_or_drop_neighbour;
559   bc->cont_cls = NULL;
560   bc->bl_pos = tcc->bl;
561   if (GNUNET_YES == tcc->first)
562   {
563     /* all would wait for the same client, no need to
564      * create more than just the first task right now */
565     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
566     tcc->first = GNUNET_NO;
567   }
568 }
569
570
571 /**
572  * Initialize a blacklisting client.  We got a blacklist-init
573  * message from this client, add him to the list of clients
574  * to query for blacklisting.
575  *
576  * @param cls unused
577  * @param client the client
578  * @param message the blacklist-init message that was sent
579  */
580 void
581 GST_blacklist_handle_init (void *cls,
582                            struct GNUNET_SERVER_Client *client,
583                            const struct GNUNET_MessageHeader *message)
584 {
585   struct Blacklisters *bl;
586   struct TestConnectionContext tcc;
587
588   for (bl = bl_head; NULL != bl; bl = bl->next)
589     if (bl->client == client)
590     {
591       GNUNET_break (0);
592       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
593       return;
594     }
595
596   GNUNET_SERVER_client_mark_monitor (client);
597   bl = GNUNET_new (struct Blacklisters);
598   bl->client = client;
599   bl->call_receive_done = GNUNET_YES;
600   GNUNET_SERVER_client_keep (client);
601   GNUNET_CONTAINER_DLL_insert_after (bl_head,
602                                      bl_tail,
603                                      bl_tail,
604                                      bl);
605   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606               "New blacklist client %p\n",
607               client);
608
609   /* confirm that all existing connections are OK! */
610   tcc.bl = bl;
611   tcc.first = GNUNET_YES;
612   GST_neighbours_iterate (&test_connection_ok, &tcc);
613 }
614
615
616 /**
617  * A blacklisting client has sent us reply. Process it.
618  *
619  * @param cls unused
620  * @param client the client
621  * @param message the blacklist-init message that was sent
622  */
623 void
624 GST_blacklist_handle_reply (void *cls,
625                             struct GNUNET_SERVER_Client *client,
626                             const struct GNUNET_MessageHeader *message)
627 {
628   const struct BlacklistMessage *msg =
629       (const struct BlacklistMessage *) message;
630   struct Blacklisters *bl;
631   struct GST_BlacklistCheck *bc;
632
633   bl = bl_head;
634   while ((bl != NULL) && (bl->client != client))
635     bl = bl->next;
636   if (NULL == bl)
637   {
638     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639                 "Blacklist client disconnected\n");
640     GNUNET_SERVER_receive_done (client,
641                                 GNUNET_SYSERR);
642     return;
643   }
644
645   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
646               "Blacklist client %p sent reply for `%s'\n",
647               client,
648               GNUNET_i2s (&msg->peer));
649
650   bc = bl->bc;
651   bl->bc = NULL;
652   bl->waiting_for_reply = GNUNET_NO;
653   bl->call_receive_done = GNUNET_YES; /* Remember to call receive_done */
654   if (NULL != bc)
655   {
656     /* only run this if the blacklist check has not been
657      * cancelled in the meantime... */
658     GNUNET_assert (bc->bl_pos == bl);
659     if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
660     {
661       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
662                   "Blacklist check failed, peer not allowed\n");
663       /* For the duration of the continuation, make the ongoing
664          check invisible (to avoid double-cancellation); then
665          add it back again so we can re-use GST_blacklist_test_cancel() */
666       GNUNET_CONTAINER_DLL_remove (bc_head,
667                                    bc_tail,
668                                    bc);
669       bc->cont (bc->cont_cls, 
670                 &bc->peer,
671                 bc->address,
672                 bc->session,
673                 GNUNET_NO);
674       GNUNET_CONTAINER_DLL_insert (bc_head,
675                                    bc_tail,
676                                    bc);
677       GST_blacklist_test_cancel (bc);
678       GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
679       bl->call_receive_done = GNUNET_NO;
680       return;
681     }
682     else
683     {
684       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685                   "Blacklist check succeeded, continuing with checks\n");
686       GNUNET_SERVER_receive_done (bl->client,
687                                   GNUNET_OK);
688       bl->call_receive_done = GNUNET_NO;
689       bc->bl_pos = bl->next;
690       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, 
691                                            bc);
692     }
693   }
694   /* check if any other blacklist checks are waiting for this blacklister */
695   for (bc = bc_head; bc != NULL; bc = bc->next)
696     if ((bc->bl_pos == bl) && (NULL == bc->task))
697     {
698       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, 
699                                            bc);
700       break;
701     }
702 }
703
704
705 /**
706  * Add the given peer to the blacklist (for the given transport).
707  *
708  * @param peer peer to blacklist
709  * @param transport_name transport to blacklist for this peer, NULL for all
710  */
711 void
712 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
713                         const char *transport_name)
714 {
715   char *transport = NULL;
716
717   if (NULL != transport_name)
718   {
719     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
720                 "Adding peer `%s' with plugin `%s' to blacklist\n",
721                 GNUNET_i2s (peer), transport_name);
722     transport = GNUNET_strdup (transport_name);
723   }
724   else
725     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
726                 "Adding peer `%s' with all plugins to blacklist\n",
727                 GNUNET_i2s (peer));
728   if (NULL == blacklist)
729     blacklist =
730       GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
731                                             GNUNET_NO);
732
733   GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
734                                      transport,
735                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
736 }
737
738
739 /**
740  * Abort blacklist if @a address and @a session match.
741  *
742  * @param address address used to abort matching checks
743  * @param session session used to abort matching checks
744  */
745 void
746 GST_blacklist_abort_matching (const struct GNUNET_HELLO_Address *address,
747                               struct GNUNET_ATS_Session *session)
748 {
749   struct GST_BlacklistCheck *bc;
750   struct GST_BlacklistCheck *n;
751
752   n = bc_head; 
753   while (NULL != (bc = n))
754   {
755     n = bc->next;
756     if ( (bc->session == session) &&
757          (0 == GNUNET_HELLO_address_cmp (bc->address,
758                                          address)) )
759     {
760       bc->cont (bc->cont_cls,
761                 &bc->peer,
762                 bc->address,
763                 bc->session,
764                 GNUNET_SYSERR);
765       GST_blacklist_test_cancel (bc);
766     }
767   }
768 }
769
770
771 /**
772  * Test if the given blacklist entry matches.  If so,
773  * abort the iteration.
774  *
775  * @param cls the transport name to match (const char*)
776  * @param key the key (unused)
777  * @param value the 'char *' (name of a blacklisted transport)
778  * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
779  */
780 static int
781 test_blacklisted (void *cls,
782                   const struct GNUNET_PeerIdentity *key,
783                   void *value)
784 {
785   const char *transport_name = cls;
786   char *be = value;
787
788   /* Blacklist entry be:
789    *  (NULL == be): peer is blacklisted with all plugins
790    *  (NULL != be): peer is blacklisted for a specific plugin
791    *
792    * If (NULL != transport_name) we look for a transport specific entry:
793    *  if (transport_name == be) forbidden
794    *
795    */
796
797   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798               "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
799               GNUNET_i2s (key),
800               (NULL == transport_name) ? "unspecified" : transport_name,
801               (NULL == be) ? "all plugins" : be);
802   /* all plugins for this peer were blacklisted: disallow */
803   if (NULL == value)
804                 return GNUNET_NO;
805
806   /* blacklist check for specific transport */
807   if ((NULL != transport_name) && (NULL != value))
808   {
809     if (0 == strcmp (transport_name,
810                      be))
811       return GNUNET_NO;           /* plugin is blacklisted! */
812   }
813   return GNUNET_OK;
814 }
815
816
817 /**
818  * Test if a peer/transport combination is blacklisted.
819  *
820  * @param peer the identity of the peer to test
821  * @param transport_name name of the transport to test, never NULL
822  * @param cont function to call with result
823  * @param cont_cls closure for @a cont
824  * @param address address to pass back to @a cont, can be NULL
825  * @param session session to pass back to @a cont, can be NULL
826  * @return handle to the blacklist check, NULL if the decision
827  *        was made instantly and @a cont was already called
828  */
829 struct GST_BlacklistCheck *
830 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
831                             const char *transport_name,
832                             GST_BlacklistTestContinuation cont,
833                             void *cont_cls,
834                             const struct GNUNET_HELLO_Address *address,
835                             struct GNUNET_ATS_Session *session)
836 {
837   struct GST_BlacklistCheck *bc;
838
839   GNUNET_assert (NULL != peer);
840   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
841               "Blacklist check for peer `%s':%s\n",
842               GNUNET_i2s (peer),
843               (NULL != transport_name) ? transport_name : "unspecified");
844
845   /* Check local blacklist by iterating over hashmap
846    * If iteration is aborted, we found a matching blacklist entry */
847   if ((NULL != blacklist) &&
848       (GNUNET_SYSERR ==
849        GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
850                                                    &test_blacklisted,
851                                                    (void *) transport_name)))
852   {
853     /* Disallowed by config, disapprove instantly */
854     GNUNET_STATISTICS_update (GST_stats,
855                               gettext_noop ("# disconnects due to blacklist"),
856                               1, 
857                               GNUNET_NO);
858     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
859                 _("Disallowing connection to peer `%s' on transport %s\n"),
860                 GNUNET_i2s (peer),
861                 (NULL != transport_name) ? transport_name : "unspecified");
862     if (NULL != cont)
863       cont (cont_cls,       
864             peer, 
865             address,
866             session,
867             GNUNET_NO);
868     return NULL;
869   }
870
871   if (NULL == bl_head)
872   {
873     /* no blacklist clients, approve instantly */
874     if (NULL != cont)
875       cont (cont_cls,
876             peer,
877             address,
878             session,
879             GNUNET_OK);
880     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
881                 "Allowing connection to peer `%s' %s\n",
882                 GNUNET_i2s (peer),
883                 (NULL != transport_name) ? transport_name : "");
884     return NULL;
885   }
886
887   /* need to query blacklist clients */
888   bc = GNUNET_new (struct GST_BlacklistCheck);
889   GNUNET_CONTAINER_DLL_insert (bc_head, 
890                                bc_tail,
891                                bc);
892   bc->peer = *peer;
893   bc->address = GNUNET_HELLO_address_copy (address);
894   bc->session = session;
895   bc->cont = cont;
896   bc->cont_cls = cont_cls;
897   bc->bl_pos = bl_head;
898   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
899   return bc;
900 }
901
902
903 /**
904  * Cancel a blacklist check.
905  *
906  * @param bc check to cancel
907  */
908 void
909 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
910 {
911   GNUNET_CONTAINER_DLL_remove (bc_head,
912                                bc_tail,
913                                bc);
914   if (NULL != bc->bl_pos)
915   {
916     if (bc->bl_pos->bc == bc)
917     {
918       /* we're at the head of the queue, remove us! */
919       bc->bl_pos->bc = NULL;
920     }
921   }
922   if (NULL != bc->task)
923   {
924     GNUNET_SCHEDULER_cancel (bc->task);
925     bc->task = NULL;
926   }
927   if (NULL != bc->th)
928   {
929     GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
930     bc->th = NULL;
931   }
932   GNUNET_free_non_null (bc->address);
933   GNUNET_free (bc);
934 }
935
936
937 /* end of file gnunet-service-transport_blacklist.c */