session end function must include address to notify address
[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       break;
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, const char *section,
254                     const char *option,
255                     const char *value)
256 {
257   unsigned int *res = cls;
258   struct GNUNET_PeerIdentity peer;
259   char *plugs;
260   char *pos;
261
262   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (option,
263                                                                   strlen (option),
264                                                                   &peer.public_key))
265     return;
266
267   if ((NULL == value) || (0 == strcmp(value, "")))
268   {
269     /* Blacklist whole peer */
270     GST_blacklist_add_peer (&peer, NULL);
271     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
272                 _("Adding blacklisting entry for peer `%s'\n"), GNUNET_i2s (&peer));
273   }
274   else
275   {
276     plugs = GNUNET_strdup (value);
277     for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
278       {
279         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
280                     _("Adding blacklisting entry for peer `%s':`%s'\n"),
281                     GNUNET_i2s (&peer), pos);
282         GST_blacklist_add_peer (&peer, pos);
283       }
284     GNUNET_free (plugs);
285   }
286   (*res)++;
287 }
288
289
290 /**
291  * Read blacklist configuration
292  *
293  * @param cfg the configuration handle
294  * @param my_id my peer identity
295  */
296 static void
297 read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
298                               const struct GNUNET_PeerIdentity *my_id)
299 {
300   char cfg_sect[512];
301   unsigned int res = 0;
302
303   GNUNET_snprintf (cfg_sect,
304                    sizeof (cfg_sect),
305                    "transport-blacklist-%s",
306                    GNUNET_i2s_full (my_id));
307   GNUNET_CONFIGURATION_iterate_section_values (cfg, cfg_sect, &blacklist_cfg_iter, &res);
308   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
309               "Loaded %u blacklisting entries from configuration\n", res);
310 }
311
312
313 /**
314  * Start blacklist subsystem.
315  *
316  * @param server server used to accept clients from
317  * @param cfg configuration handle
318  * @param my_id my peer id
319  */
320 void
321 GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
322                      const struct GNUNET_CONFIGURATION_Handle *cfg,
323                      const struct GNUNET_PeerIdentity *my_id)
324 {
325   GNUNET_assert (NULL != cfg);
326   GNUNET_assert (NULL != my_id);
327   read_blacklist_configuration (cfg, my_id);
328   GNUNET_SERVER_disconnect_notify (server, &client_disconnect_notification,
329                                    NULL);
330 }
331
332
333 /**
334  * Free the given entry in the blacklist.
335  *
336  * @param cls unused
337  * @param key host identity (unused)
338  * @param value the blacklist entry
339  * @return GNUNET_OK (continue to iterate)
340  */
341 static int
342 free_blacklist_entry (void *cls,
343                       const struct GNUNET_PeerIdentity *key,
344                       void *value)
345 {
346   char *be = value;
347
348   GNUNET_free_non_null (be);
349   return GNUNET_OK;
350 }
351
352
353 /**
354  * Stop blacklist subsystem.
355  */
356 void
357 GST_blacklist_stop ()
358 {
359   if (NULL != blacklist)
360   {
361     GNUNET_CONTAINER_multipeermap_iterate (blacklist, &free_blacklist_entry,
362                                            NULL);
363     GNUNET_CONTAINER_multipeermap_destroy (blacklist);
364     blacklist = NULL;
365   }
366 }
367
368
369 /**
370  * Transmit blacklist query to the client.
371  *
372  * @param cls the 'struct GST_BlacklistCheck'
373  * @param size number of bytes allowed
374  * @param buf where to copy the message
375  * @return number of bytes copied to buf
376  */
377 static size_t
378 transmit_blacklist_message (void *cls, size_t size, void *buf)
379 {
380   struct GST_BlacklistCheck *bc = cls;
381   struct Blacklisters *bl;
382   struct BlacklistMessage bm;
383
384   bc->th = NULL;
385   if (size == 0)
386   {
387     GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
388     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
389     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
390                 "Failed to send blacklist test for peer `%s' to client\n",
391                 GNUNET_i2s (&bc->peer));
392     return 0;
393   }
394   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395               "Sending blacklist test for peer `%s' to client %p\n",
396               GNUNET_i2s (&bc->peer), bc->bl_pos->client);
397   bl = bc->bl_pos;
398   bm.header.size = htons (sizeof (struct BlacklistMessage));
399   bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
400   bm.is_allowed = htonl (0);
401   bm.peer = bc->peer;
402   memcpy (buf, &bm, sizeof (bm));
403   if (GNUNET_YES == bl->call_receive_done)
404   {
405     GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
406     bl->call_receive_done = GNUNET_NO;
407   }
408
409   bl->waiting_for_reply = GNUNET_YES;
410   return sizeof (bm);
411 }
412
413
414 /**
415  * Perform next action in the blacklist check.
416  *
417  * @param cls the 'struct GST_BlacklistCheck*'
418  * @param tc unused
419  */
420 static void
421 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
422 {
423   struct GST_BlacklistCheck *bc = cls;
424   struct Blacklisters *bl;
425
426   bc->task = GNUNET_SCHEDULER_NO_TASK;
427   bl = bc->bl_pos;
428   if (bl == NULL)
429   {
430     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431                 "No other blacklist clients active, will allow neighbour `%s'\n",
432                 GNUNET_i2s (&bc->peer));
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       bc->bl_pos = bc->bl_pos->next;
623       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
624     }
625   }
626   /* check if any other blacklist checks are waiting for this blacklister */
627   for (bc = bc_head; bc != NULL; bc = bc->next)
628     if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
629     {
630       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
631       break;
632     }
633 }
634
635
636 /**
637  * Add the given peer to the blacklist (for the given transport).
638  *
639  * @param peer peer to blacklist
640  * @param transport_name transport to blacklist for this peer, NULL for all
641  */
642 void
643 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
644                         const char *transport_name)
645 {
646   char * transport = NULL;
647
648   if (NULL != transport_name)
649   {
650     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
651                 "Adding peer `%s' with plugin `%s' to blacklist\n",
652                 GNUNET_i2s (peer), transport_name);
653     transport = GNUNET_strdup (transport_name);
654   }
655   else
656     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
657                 "Adding peer `%s' with all plugins to blacklist\n",
658                 GNUNET_i2s (peer));
659   if (blacklist == NULL)
660     blacklist =
661       GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
662                                             GNUNET_NO);
663
664   GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
665                                      transport,
666                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
667 }
668
669
670 /**
671  * Test if the given blacklist entry matches.  If so,
672  * abort the iteration.
673  *
674  * @param cls the transport name to match (const char*)
675  * @param key the key (unused)
676  * @param value the 'char *' (name of a blacklisted transport)
677  * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
678  */
679 static int
680 test_blacklisted (void *cls,
681                   const struct GNUNET_PeerIdentity *key,
682                   void *value)
683 {
684   const char *transport_name = cls;
685   char *be = value;
686
687   /* Blacklist entry be:
688    *  (NULL == be): peer is blacklisted with all plugins
689    *  (NULL != be): peer is blacklisted for a specific plugin
690    *
691    * If (NULL != transport_name) we look for a transport specific entry:
692    *  if (transport_name == be) forbidden
693    *
694    */
695
696   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697               "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
698               GNUNET_i2s (key),
699               (NULL == transport_name) ? "unspecified" : transport_name,
700               (NULL == be) ? "all plugins" : be);
701   /* all plugins for this peer were blacklisted: disallow */
702   if (NULL == value)
703                 return GNUNET_NO;
704
705   /* blacklist check for specific transport */
706   if ((NULL != transport_name) && (NULL != value))
707   {
708         if (0 == strcmp (transport_name, be))
709                         return GNUNET_NO;           /* plugin is blacklisted! */
710   }
711   return GNUNET_OK;
712 }
713
714
715 /**
716  * Test if a peer/transport combination is blacklisted.
717  *
718  * @param peer the identity of the peer to test
719  * @param transport_name name of the transport to test, never NULL
720  * @param cont function to call with result
721  * @param cont_cls closure for 'cont'
722  * @return handle to the blacklist check, NULL if the decision
723  *        was made instantly and 'cont' was already called
724  */
725 struct GST_BlacklistCheck *
726 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
727                             const char *transport_name,
728                             GST_BlacklistTestContinuation cont, void *cont_cls)
729 {
730   struct GST_BlacklistCheck *bc;
731
732   GNUNET_assert (peer != NULL);
733   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist check for peer `%s':%s\n",
734                 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
735
736   /* Check local blacklist by iterating over hashmap
737    * If iteration is aborted, we found a matching blacklist entry */
738   if ((blacklist != NULL) &&
739       (GNUNET_SYSERR ==
740        GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
741                                                    &test_blacklisted,
742                                                    (void *) transport_name)))
743   {
744     /* Disallowed by config, disapprove instantly */
745     GNUNET_STATISTICS_update (GST_stats,
746                               gettext_noop ("# disconnects due to blacklist"),
747                               1, GNUNET_NO);
748     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disallowing connection to peer `%s' on transport %s\n",
749                 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
750     if (cont != NULL)
751       cont (cont_cls, peer, GNUNET_NO);
752     return NULL;
753   }
754
755   if (bl_head == NULL)
756   {
757     /* no blacklist clients, approve instantly */
758     if (cont != NULL)
759       cont (cont_cls, peer, GNUNET_OK);
760     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Allowing connection to peer `%s' %s\n",
761                 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "");
762     return NULL;
763   }
764
765   /* need to query blacklist clients */
766   bc = GNUNET_new (struct GST_BlacklistCheck);
767   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
768   bc->peer = *peer;
769   bc->cont = cont;
770   bc->cont_cls = cont_cls;
771   bc->bl_pos = bl_head;
772   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
773   return bc;
774 }
775
776
777 /**
778  * Cancel a blacklist check.
779  *
780  * @param bc check to cancel
781  */
782 void
783 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
784 {
785   GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
786   if (bc->bl_pos != NULL)
787   {
788     if (bc->bl_pos->bc == bc)
789     {
790       /* we're at the head of the queue, remove us! */
791       bc->bl_pos->bc = NULL;
792     }
793   }
794   if (GNUNET_SCHEDULER_NO_TASK != bc->task)
795   {
796     GNUNET_SCHEDULER_cancel (bc->task);
797     bc->task = GNUNET_SCHEDULER_NO_TASK;
798   }
799   if (NULL != bc->th)
800   {
801     GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
802     bc->th = NULL;
803   }
804   GNUNET_free (bc);
805 }
806
807
808 /* end of file gnunet-service-transport_blacklist.c */