-indentation
[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     bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK);
433     GNUNET_CONTAINER_DLL_remove(bc_head, bc_tail, bc);
434     GNUNET_free (bc);
435     return;
436   }
437   if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO))
438     return;                     /* someone else busy with this client */
439   bl->bc = bc;
440   bc->th =
441       GNUNET_SERVER_notify_transmit_ready (bl->client,
442                                            sizeof (struct BlacklistMessage),
443                                            GNUNET_TIME_UNIT_FOREVER_REL,
444                                            &transmit_blacklist_message, bc);
445 }
446
447
448 /**
449  * Got the result about an existing connection from a new blacklister.
450  * Shutdown the neighbour if necessary.
451  *
452  * @param cls unused
453  * @param peer the neighbour that was investigated
454  * @param allowed GNUNET_OK if we can keep it,
455  *                GNUNET_NO if we must shutdown the connection
456  */
457 static void
458 confirm_or_drop_neighbour (void *cls, const struct GNUNET_PeerIdentity *peer,
459                            int allowed)
460 {
461   if (GNUNET_OK == allowed)
462     return;                     /* we're done */
463   GNUNET_STATISTICS_update (GST_stats,
464                             gettext_noop ("# disconnects due to blacklist"), 1,
465                             GNUNET_NO);
466   GST_neighbours_force_disconnect (peer);
467 }
468
469
470 /**
471  * Closure for 'test_connection_ok'.
472  */
473 struct TestConnectionContext
474 {
475   /**
476    * Is this the first neighbour we're checking?
477    */
478   int first;
479
480   /**
481    * Handle to the blacklisting client we need to ask.
482    */
483   struct Blacklisters *bl;
484 };
485
486 /**
487  * Test if an existing connection is still acceptable given a new
488  * blacklisting client.
489  *
490  * @param cls the 'struct TestConnectionContest'
491  * @param peer neighbour's identity
492  * @param address the address
493  * @param state current state this peer is in
494  * @param state_timeout timeout for the current state of the peer
495  * @param bandwidth_in bandwidth assigned inbound
496  * @param bandwidth_out bandwidth assigned outbound
497  */
498 static void
499 test_connection_ok (void *cls, const struct GNUNET_PeerIdentity *peer,
500     const struct GNUNET_HELLO_Address *address,
501     enum GNUNET_TRANSPORT_PeerState state,
502     struct GNUNET_TIME_Absolute state_timeout,
503     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
504     struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
505 {
506   struct TestConnectionContext *tcc = cls;
507   struct GST_BlacklistCheck *bc;
508
509   bc = GNUNET_new (struct GST_BlacklistCheck);
510   GNUNET_CONTAINER_DLL_insert(bc_head, bc_tail, bc);
511   bc->peer = *peer;
512   bc->cont = &confirm_or_drop_neighbour;
513   bc->cont_cls = NULL;
514   bc->bl_pos = tcc->bl;
515   if (GNUNET_YES == tcc->first)
516   {
517     /* all would wait for the same client, no need to
518      * create more than just the first task right now */
519     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
520     tcc->first = GNUNET_NO;
521   }
522 }
523
524
525 /**
526  * Initialize a blacklisting client.  We got a blacklist-init
527  * message from this client, add him to the list of clients
528  * to query for blacklisting.
529  *
530  * @param cls unused
531  * @param client the client
532  * @param message the blacklist-init message that was sent
533  */
534 void
535 GST_blacklist_handle_init (void *cls, struct GNUNET_SERVER_Client *client,
536                            const struct GNUNET_MessageHeader *message)
537 {
538   struct Blacklisters *bl;
539   struct TestConnectionContext tcc;
540
541   bl = bl_head;
542   while (bl != NULL)
543   {
544     if (bl->client == client)
545     {
546       GNUNET_break (0);
547       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
548       return;
549     }
550     bl = bl->next;
551   }
552
553   GNUNET_SERVER_client_mark_monitor (client);
554   bl = GNUNET_new (struct Blacklisters);
555   bl->client = client;
556   bl->call_receive_done = GNUNET_YES;
557   GNUNET_SERVER_client_keep (client);
558   GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
559
560   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New blacklist client %p\n", client);
561
562   /* confirm that all existing connections are OK! */
563   tcc.bl = bl;
564   tcc.first = GNUNET_YES;
565   GST_neighbours_iterate (&test_connection_ok, &tcc);
566 }
567
568
569 /**
570  * A blacklisting client has sent us reply. Process it.
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_reply (void *cls, struct GNUNET_SERVER_Client *client,
578                             const struct GNUNET_MessageHeader *message)
579 {
580   const struct BlacklistMessage *msg =
581       (const struct BlacklistMessage *) message;
582   struct Blacklisters *bl;
583   struct GST_BlacklistCheck *bc;
584
585   bl = bl_head;
586   while ((bl != NULL) && (bl->client != client))
587     bl = bl->next;
588   if (bl == NULL)
589   {
590     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n");
591     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
592     return;
593   }
594
595   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client %p sent reply for `%s'\n",
596       client, GNUNET_i2s(&msg->peer));
597
598   bc = bl->bc;
599   bl->bc = NULL;
600   bl->waiting_for_reply = GNUNET_NO;
601   bl->call_receive_done = GNUNET_YES; /* Remember to call receive_done */
602   if (NULL != bc)
603   {
604     /* only run this if the blacklist check has not been
605      * cancelled in the meantime... */
606     if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
607     {
608       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609                   "Blacklist check failed, peer not allowed\n");
610       bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
611       GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
612       GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
613       bl->call_receive_done = GNUNET_NO;
614       GNUNET_free (bc);
615       return;
616     }
617     else
618     {
619       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
620                   "Blacklist check succeeded, continuing with checks\n");
621       bc->bl_pos = bc->bl_pos->next;
622       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
623     }
624   }
625   /* check if any other blacklist checks are waiting for this blacklister */
626   for (bc = bc_head; bc != NULL; bc = bc->next)
627     if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
628     {
629       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
630       break;
631     }
632 }
633
634
635 /**
636  * Add the given peer to the blacklist (for the given transport).
637  *
638  * @param peer peer to blacklist
639  * @param transport_name transport to blacklist for this peer, NULL for all
640  */
641 void
642 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
643                         const char *transport_name)
644 {
645   char * transport = NULL;
646
647   if (NULL != transport_name)
648   {
649     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
650                 "Adding peer `%s' with plugin `%s' to blacklist\n",
651                 GNUNET_i2s (peer), transport_name);
652     transport = GNUNET_strdup (transport_name);
653   }
654   else
655     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
656                 "Adding peer `%s' with all plugins to blacklist\n",
657                 GNUNET_i2s (peer));
658   if (blacklist == NULL)
659     blacklist =
660       GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
661                                             GNUNET_NO);
662
663   GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
664                                      transport,
665                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
666 }
667
668
669 /**
670  * Test if the given blacklist entry matches.  If so,
671  * abort the iteration.
672  *
673  * @param cls the transport name to match (const char*)
674  * @param key the key (unused)
675  * @param value the 'char *' (name of a blacklisted transport)
676  * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
677  */
678 static int
679 test_blacklisted (void *cls,
680                   const struct GNUNET_PeerIdentity *key,
681                   void *value)
682 {
683   const char *transport_name = cls;
684   char *be = value;
685
686   /* Blacklist entry be:
687    *  (NULL == be): peer is blacklisted with all plugins
688    *  (NULL != be): peer is blacklisted for a specific plugin
689    *
690    * If (NULL != transport_name) we look for a transport specific entry:
691    *  if (transport_name == be) forbidden
692    *
693    */
694
695   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
696               "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
697               GNUNET_i2s (key),
698               (NULL == transport_name) ? "unspecified" : transport_name,
699               (NULL == be) ? "all plugins" : be);
700   /* all plugins for this peer were blacklisted: disallow */
701   if (NULL == value)
702                 return GNUNET_NO;
703
704   /* blacklist check for specific transport */
705   if ((NULL != transport_name) && (NULL != value))
706   {
707         if (0 == strcmp (transport_name, be))
708                         return GNUNET_NO;           /* plugin is blacklisted! */
709   }
710   return GNUNET_OK;
711 }
712
713
714 /**
715  * Test if a peer/transport combination is blacklisted.
716  *
717  * @param peer the identity of the peer to test
718  * @param transport_name name of the transport to test, never NULL
719  * @param cont function to call with result
720  * @param cont_cls closure for 'cont'
721  * @return handle to the blacklist check, NULL if the decision
722  *        was made instantly and 'cont' was already called
723  */
724 struct GST_BlacklistCheck *
725 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
726                             const char *transport_name,
727                             GST_BlacklistTestContinuation cont, void *cont_cls)
728 {
729   struct GST_BlacklistCheck *bc;
730
731   GNUNET_assert (peer != NULL);
732   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist check for peer `%s':%s\n",
733                 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
734
735   /* Check local blacklist by iterating over hashmap
736    * If iteration is aborted, we found a matching blacklist entry */
737   if ((blacklist != NULL) &&
738       (GNUNET_SYSERR ==
739        GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
740                                                    &test_blacklisted,
741                                                    (void *) transport_name)))
742   {
743     /* Disallowed by config, disapprove instantly */
744     GNUNET_STATISTICS_update (GST_stats,
745                               gettext_noop ("# disconnects due to blacklist"),
746                               1, GNUNET_NO);
747     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disallowing connection to peer `%s' on transport %s\n",
748                 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
749     if (cont != NULL)
750       cont (cont_cls, peer, GNUNET_NO);
751     return NULL;
752   }
753
754   if (bl_head == NULL)
755   {
756     /* no blacklist clients, approve instantly */
757     if (cont != NULL)
758       cont (cont_cls, peer, GNUNET_OK);
759     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Allowing connection to peer `%s' %s\n",
760                 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "");
761     return NULL;
762   }
763
764   /* need to query blacklist clients */
765   bc = GNUNET_new (struct GST_BlacklistCheck);
766   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
767   bc->peer = *peer;
768   bc->cont = cont;
769   bc->cont_cls = cont_cls;
770   bc->bl_pos = bl_head;
771   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
772   return bc;
773 }
774
775
776 /**
777  * Cancel a blacklist check.
778  *
779  * @param bc check to cancel
780  */
781 void
782 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
783 {
784   GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
785   if (bc->bl_pos != NULL)
786   {
787     if (bc->bl_pos->bc == bc)
788     {
789       /* we're at the head of the queue, remove us! */
790       bc->bl_pos->bc = NULL;
791     }
792   }
793   if (GNUNET_SCHEDULER_NO_TASK != bc->task)
794   {
795     GNUNET_SCHEDULER_cancel (bc->task);
796     bc->task = GNUNET_SCHEDULER_NO_TASK;
797   }
798   if (NULL != bc->th)
799   {
800     GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
801     bc->th = NULL;
802   }
803   GNUNET_free (bc);
804 }
805
806
807 /* end of file gnunet-service-transport_blacklist.c */