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