2 This file is part of GNUnet.
3 (C) 2010,2011 Christian Grothoff (and other contributing authors)
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.
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.
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.
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.
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
35 * To blacklist connections to P565... on peer AG2P... using tcp add:
36 * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
37 * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G = tcp
39 * To blacklist connections to P565... on peer AG2P... using all plugins add:
40 * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
41 * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G =
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.
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
58 #include "gnunet-service-transport.h"
59 #include "gnunet-service-transport_blacklist.h"
60 #include "gnunet-service-transport_neighbours.h"
61 #include "transport.h"
64 * Size of the blacklist hash map.
66 #define TRANSPORT_BLACKLIST_HT_SIZE 64
70 * Context we use when performing a blacklist check.
72 struct GST_BlacklistCheck;
76 * Information kept for each client registered to perform
82 * This is a linked list.
84 struct Blacklisters *next;
87 * This is a linked list.
89 struct Blacklisters *prev;
92 * Client responsible for this entry.
94 struct GNUNET_SERVER_Client *client;
97 * Blacklist check that we're currently performing (or NULL
98 * if we're performing one that has been cancelled).
100 struct GST_BlacklistCheck *bc;
103 * Set to GNUNET_YES if we're currently waiting for a reply.
105 int waiting_for_reply;
108 * GNUNET_YES if we have to call receive_done for this client
110 int call_receive_done;
117 * Context we use when performing a blacklist check.
119 struct GST_BlacklistCheck
123 * This is a linked list.
125 struct GST_BlacklistCheck *next;
128 * This is a linked list.
130 struct GST_BlacklistCheck *prev;
133 * Peer being checked.
135 struct GNUNET_PeerIdentity peer;
138 * Continuation to call with the result.
140 GST_BlacklistTestContinuation cont;
148 * Current transmission request handle for this client, or NULL if no
149 * request is pending.
151 struct GNUNET_SERVER_TransmitHandle *th;
154 * Our current position in the blacklisters list.
156 struct Blacklisters *bl_pos;
159 * Current task performing the check.
161 GNUNET_SCHEDULER_TaskIdentifier task;
167 * Head of DLL of active blacklisting queries.
169 static struct GST_BlacklistCheck *bc_head;
172 * Tail of DLL of active blacklisting queries.
174 static struct GST_BlacklistCheck *bc_tail;
177 * Head of DLL of blacklisting clients.
179 static struct Blacklisters *bl_head;
182 * Tail of DLL of blacklisting clients.
184 static struct Blacklisters *bl_tail;
187 * Hashmap of blacklisted peers. Values are of type 'char *' (transport names),
188 * can be NULL if we have no static blacklist.
190 static struct GNUNET_CONTAINER_MultiPeerMap *blacklist;
194 * Perform next action in the blacklist check.
196 * @param cls the 'struct BlacklistCheck*'
200 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
204 * Called whenever a client is disconnected. Frees our
205 * resources associated with that client.
207 * @param cls closure (unused)
208 * @param client identification of the client
211 client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client)
213 struct Blacklisters *bl;
214 struct GST_BlacklistCheck *bc;
218 for (bl = bl_head; bl != NULL; bl = bl->next)
220 if (bl->client != client)
222 for (bc = bc_head; bc != NULL; bc = bc->next)
224 if (bc->bl_pos != bl)
226 bc->bl_pos = bl->next;
229 GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
232 if (bc->task == GNUNET_SCHEDULER_NO_TASK)
233 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
235 GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
236 GNUNET_SERVER_client_drop (bl->client);
244 * Function to iterate over options in the blacklisting section for a peer.
247 * @param section name of the section
248 * @param option name of the option
249 * @param value value of the option
252 blacklist_cfg_iter (void *cls, const char *section,
256 unsigned int *res = cls;
257 struct GNUNET_PeerIdentity peer;
261 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (option,
266 if ((NULL == value) || (0 == strcmp(value, "")))
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));
275 plugs = GNUNET_strdup (value);
276 for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
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);
290 * Read blacklist configuration
292 * @param cfg the configuration handle
293 * @param my_id my peer identity
296 read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
297 const struct GNUNET_PeerIdentity *my_id)
300 unsigned int res = 0;
302 GNUNET_snprintf (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);
313 * Start blacklist subsystem.
315 * @param server server used to accept clients from
316 * @param cfg configuration handle
317 * @param my_id my peer id
320 GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
321 const struct GNUNET_CONFIGURATION_Handle *cfg,
322 const struct GNUNET_PeerIdentity *my_id)
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,
333 * Free the given entry in the blacklist.
336 * @param key host identity (unused)
337 * @param value the blacklist entry
338 * @return GNUNET_OK (continue to iterate)
341 free_blacklist_entry (void *cls,
342 const struct GNUNET_PeerIdentity *key,
347 GNUNET_free_non_null (be);
353 * Stop blacklist subsystem.
356 GST_blacklist_stop ()
358 if (NULL != blacklist)
360 GNUNET_CONTAINER_multipeermap_iterate (blacklist, &free_blacklist_entry,
362 GNUNET_CONTAINER_multipeermap_destroy (blacklist);
369 * Transmit blacklist query to the client.
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
377 transmit_blacklist_message (void *cls, size_t size, void *buf)
379 struct GST_BlacklistCheck *bc = cls;
380 struct Blacklisters *bl;
381 struct BlacklistMessage bm;
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));
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);
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);
401 memcpy (buf, &bm, sizeof (bm));
402 if (GNUNET_YES == bl->call_receive_done)
404 GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
405 bl->call_receive_done = GNUNET_NO;
408 bl->waiting_for_reply = GNUNET_YES;
414 * Perform next action in the blacklist check.
416 * @param cls the 'struct GST_BlacklistCheck*'
420 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
422 struct GST_BlacklistCheck *bc = cls;
423 struct Blacklisters *bl;
425 bc->task = GNUNET_SCHEDULER_NO_TASK;
429 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430 "No other blacklist clients active, will allow neighbour `%s'\n",
431 GNUNET_i2s (&bc->peer));
433 bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK);
434 GNUNET_CONTAINER_DLL_remove(bc_head, bc_tail, bc);
438 if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO))
439 return; /* someone else busy with this client */
442 GNUNET_SERVER_notify_transmit_ready (bl->client,
443 sizeof (struct BlacklistMessage),
444 GNUNET_TIME_UNIT_FOREVER_REL,
445 &transmit_blacklist_message, bc);
450 * Got the result about an existing connection from a new blacklister.
451 * Shutdown the neighbour if necessary.
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
459 confirm_or_drop_neighbour (void *cls, const struct GNUNET_PeerIdentity *peer,
462 if (GNUNET_OK == allowed)
463 return; /* we're done */
464 GNUNET_STATISTICS_update (GST_stats,
465 gettext_noop ("# disconnects due to blacklist"), 1,
467 GST_neighbours_force_disconnect (peer);
472 * Closure for 'test_connection_ok'.
474 struct TestConnectionContext
477 * Is this the first neighbour we're checking?
482 * Handle to the blacklisting client we need to ask.
484 struct Blacklisters *bl;
488 * Test if an existing connection is still acceptable given a new
489 * blacklisting client.
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
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)
507 struct TestConnectionContext *tcc = cls;
508 struct GST_BlacklistCheck *bc;
510 bc = GNUNET_new (struct GST_BlacklistCheck);
511 GNUNET_CONTAINER_DLL_insert(bc_head, bc_tail, bc);
513 bc->cont = &confirm_or_drop_neighbour;
515 bc->bl_pos = tcc->bl;
516 if (GNUNET_YES == tcc->first)
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;
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.
532 * @param client the client
533 * @param message the blacklist-init message that was sent
536 GST_blacklist_handle_init (void *cls, struct GNUNET_SERVER_Client *client,
537 const struct GNUNET_MessageHeader *message)
539 struct Blacklisters *bl;
540 struct TestConnectionContext tcc;
545 if (bl->client == client)
548 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
554 GNUNET_SERVER_client_mark_monitor (client);
555 bl = GNUNET_new (struct Blacklisters);
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);
561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New blacklist client %p\n", client);
563 /* confirm that all existing connections are OK! */
565 tcc.first = GNUNET_YES;
566 GST_neighbours_iterate (&test_connection_ok, &tcc);
571 * A blacklisting client has sent us reply. Process it.
574 * @param client the client
575 * @param message the blacklist-init message that was sent
578 GST_blacklist_handle_reply (void *cls, struct GNUNET_SERVER_Client *client,
579 const struct GNUNET_MessageHeader *message)
581 const struct BlacklistMessage *msg =
582 (const struct BlacklistMessage *) message;
583 struct Blacklisters *bl;
584 struct GST_BlacklistCheck *bc;
587 while ((bl != NULL) && (bl->client != client))
591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n");
592 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client %p sent reply for `%s'\n",
597 client, GNUNET_i2s(&msg->peer));
601 bl->waiting_for_reply = GNUNET_NO;
602 bl->call_receive_done = GNUNET_YES; /* Remember to call receive_done */
605 /* only run this if the blacklist check has not been
606 * cancelled in the meantime... */
607 if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
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;
620 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
621 "Blacklist check succeeded, continuing with checks\n");
622 GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
623 bl->call_receive_done = GNUNET_NO;
624 bc->bl_pos = bc->bl_pos->next;
625 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
628 /* check if any other blacklist checks are waiting for this blacklister */
629 for (bc = bc_head; bc != NULL; bc = bc->next)
630 if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
632 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
639 * Add the given peer to the blacklist (for the given transport).
641 * @param peer peer to blacklist
642 * @param transport_name transport to blacklist for this peer, NULL for all
645 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
646 const char *transport_name)
648 char * transport = NULL;
650 if (NULL != transport_name)
652 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
653 "Adding peer `%s' with plugin `%s' to blacklist\n",
654 GNUNET_i2s (peer), transport_name);
655 transport = GNUNET_strdup (transport_name);
658 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
659 "Adding peer `%s' with all plugins to blacklist\n",
661 if (blacklist == NULL)
663 GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
666 GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
668 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
673 * Test if the given blacklist entry matches. If so,
674 * abort the iteration.
676 * @param cls the transport name to match (const char*)
677 * @param key the key (unused)
678 * @param value the 'char *' (name of a blacklisted transport)
679 * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
682 test_blacklisted (void *cls,
683 const struct GNUNET_PeerIdentity *key,
686 const char *transport_name = cls;
689 /* Blacklist entry be:
690 * (NULL == be): peer is blacklisted with all plugins
691 * (NULL != be): peer is blacklisted for a specific plugin
693 * If (NULL != transport_name) we look for a transport specific entry:
694 * if (transport_name == be) forbidden
698 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
699 "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
701 (NULL == transport_name) ? "unspecified" : transport_name,
702 (NULL == be) ? "all plugins" : be);
703 /* all plugins for this peer were blacklisted: disallow */
707 /* blacklist check for specific transport */
708 if ((NULL != transport_name) && (NULL != value))
710 if (0 == strcmp (transport_name, be))
711 return GNUNET_NO; /* plugin is blacklisted! */
718 * Test if a peer/transport combination is blacklisted.
720 * @param peer the identity of the peer to test
721 * @param transport_name name of the transport to test, never NULL
722 * @param cont function to call with result
723 * @param cont_cls closure for 'cont'
724 * @return handle to the blacklist check, NULL if the decision
725 * was made instantly and 'cont' was already called
727 struct GST_BlacklistCheck *
728 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
729 const char *transport_name,
730 GST_BlacklistTestContinuation cont, void *cont_cls)
732 struct GST_BlacklistCheck *bc;
734 GNUNET_assert (peer != NULL);
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist check for peer `%s':%s\n",
736 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
738 /* Check local blacklist by iterating over hashmap
739 * If iteration is aborted, we found a matching blacklist entry */
740 if ((blacklist != NULL) &&
742 GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
744 (void *) transport_name)))
746 /* Disallowed by config, disapprove instantly */
747 GNUNET_STATISTICS_update (GST_stats,
748 gettext_noop ("# disconnects due to blacklist"),
750 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disallowing connection to peer `%s' on transport %s\n",
751 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
753 cont (cont_cls, peer, GNUNET_NO);
759 /* no blacklist clients, approve instantly */
761 cont (cont_cls, peer, GNUNET_OK);
762 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Allowing connection to peer `%s' %s\n",
763 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "");
767 /* need to query blacklist clients */
768 bc = GNUNET_new (struct GST_BlacklistCheck);
769 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
772 bc->cont_cls = cont_cls;
773 bc->bl_pos = bl_head;
774 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
780 * Cancel a blacklist check.
782 * @param bc check to cancel
785 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
787 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
788 if (bc->bl_pos != NULL)
790 if (bc->bl_pos->bc == bc)
792 /* we're at the head of the queue, remove us! */
793 bc->bl_pos->bc = NULL;
796 if (GNUNET_SCHEDULER_NO_TASK != bc->task)
798 GNUNET_SCHEDULER_cancel (bc->task);
799 bc->task = GNUNET_SCHEDULER_NO_TASK;
803 GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
810 /* end of file gnunet-service-transport_blacklist.c */