2 This file is part of GNUnet.
3 Copyright (C) 2010,2011 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
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.
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
36 * To blacklist connections to P565... on peer AG2P... using tcp add:
37 * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
38 * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G = tcp
40 * To blacklist connections to P565... on peer AG2P... using all plugins add:
41 * [transport-blacklist-AG2PHES1BARB9IJCPAMJTFPVJ5V3A72S3F2A8SBUB8DAQ2V0O3V8G6G2JU56FHGFOHMQVKBSQFV98TCGTC3RJ1NINP82G0RC00N1520]
42 * P565723JO1C2HSN6J29TAQ22MN6CI8HTMUU55T0FUQG4CMDGGEQ8UCNBKUMB94GC8R9G4FB2SF9LDOBAJ6AMINBP4JHHDD6L7VD801G =
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.
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
59 #include "gnunet-service-transport.h"
60 #include "gnunet-service-transport_blacklist.h"
61 #include "gnunet-service-transport_neighbours.h"
62 #include "transport.h"
65 * Size of the blacklist hash map.
67 #define TRANSPORT_BLACKLIST_HT_SIZE 64
71 * Context we use when performing a blacklist check.
73 struct GST_BlacklistCheck;
77 * Information kept for each client registered to perform
83 * This is a linked list.
85 struct Blacklisters *next;
88 * This is a linked list.
90 struct Blacklisters *prev;
93 * Client responsible for this entry.
95 struct GNUNET_SERVER_Client *client;
98 * Blacklist check that we're currently performing (or NULL
99 * if we're performing one that has been cancelled).
101 struct GST_BlacklistCheck *bc;
104 * Set to #GNUNET_YES if we're currently waiting for a reply.
106 int waiting_for_reply;
109 * #GNUNET_YES if we have to call receive_done for this client
111 int call_receive_done;
118 * Context we use when performing a blacklist check.
120 struct GST_BlacklistCheck
124 * This is a linked list.
126 struct GST_BlacklistCheck *next;
129 * This is a linked list.
131 struct GST_BlacklistCheck *prev;
134 * Peer being checked.
136 struct GNUNET_PeerIdentity peer;
139 * Continuation to call with the result.
141 GST_BlacklistTestContinuation cont;
144 * Closure for @e cont.
149 * Address for #GST_blacklist_abort_matching(), can be NULL.
151 struct GNUNET_HELLO_Address *address;
154 * Session for #GST_blacklist_abort_matching(), can be NULL.
156 struct GNUNET_ATS_Session *session;
159 * Current transmission request handle for this client, or NULL if no
160 * request is pending.
162 struct GNUNET_SERVER_TransmitHandle *th;
165 * Our current position in the blacklisters list.
167 struct Blacklisters *bl_pos;
170 * Current task performing the check.
172 struct GNUNET_SCHEDULER_Task *task;
178 * Head of DLL of active blacklisting queries.
180 static struct GST_BlacklistCheck *bc_head;
183 * Tail of DLL of active blacklisting queries.
185 static struct GST_BlacklistCheck *bc_tail;
188 * Head of DLL of blacklisting clients.
190 static struct Blacklisters *bl_head;
193 * Tail of DLL of blacklisting clients.
195 static struct Blacklisters *bl_tail;
198 * Hashmap of blacklisted peers. Values are of type 'char *' (transport names),
199 * can be NULL if we have no static blacklist.
201 static struct GNUNET_CONTAINER_MultiPeerMap *blacklist;
205 * Perform next action in the blacklist check.
207 * @param cls the `struct BlacklistCheck*`
211 do_blacklist_check (void *cls,
212 const struct GNUNET_SCHEDULER_TaskContext *tc);
216 * Called whenever a client is disconnected. Frees our
217 * resources associated with that client.
219 * @param cls closure (unused)
220 * @param client identification of the client
223 client_disconnect_notification (void *cls,
224 struct GNUNET_SERVER_Client *client)
226 struct Blacklisters *bl;
227 struct GST_BlacklistCheck *bc;
231 for (bl = bl_head; bl != NULL; bl = bl->next)
233 if (bl->client != client)
235 for (bc = bc_head; NULL != bc; bc = bc->next)
237 if (bc->bl_pos != bl)
239 bc->bl_pos = bl->next;
242 GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
245 if (NULL == bc->task)
246 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
248 GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
249 GNUNET_SERVER_client_drop (bl->client);
257 * Function to iterate over options in the blacklisting section for a peer.
260 * @param section name of the section
261 * @param option name of the option
262 * @param value value of the option
265 blacklist_cfg_iter (void *cls,
270 unsigned int *res = cls;
271 struct GNUNET_PeerIdentity peer;
276 GNUNET_CRYPTO_eddsa_public_key_from_string (option,
281 if ((NULL == value) || (0 == strcmp(value, "")))
283 /* Blacklist whole peer */
284 GST_blacklist_add_peer (&peer, NULL);
285 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
286 _("Adding blacklisting entry for peer `%s'\n"),
291 plugs = GNUNET_strdup (value);
292 for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
294 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
295 _("Adding blacklisting entry for peer `%s':`%s'\n"),
296 GNUNET_i2s (&peer), pos);
297 GST_blacklist_add_peer (&peer, pos);
306 * Read blacklist configuration
308 * @param cfg the configuration handle
309 * @param my_id my peer identity
312 read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
313 const struct GNUNET_PeerIdentity *my_id)
316 unsigned int res = 0;
318 GNUNET_snprintf (cfg_sect,
320 "transport-blacklist-%s",
321 GNUNET_i2s_full (my_id));
322 GNUNET_CONFIGURATION_iterate_section_values (cfg,
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327 "Loaded %u blacklisting entries from configuration\n",
333 * Start blacklist subsystem.
335 * @param server server used to accept clients from
336 * @param cfg configuration handle
337 * @param my_id my peer id
340 GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
341 const struct GNUNET_CONFIGURATION_Handle *cfg,
342 const struct GNUNET_PeerIdentity *my_id)
344 GNUNET_assert (NULL != cfg);
345 GNUNET_assert (NULL != my_id);
346 read_blacklist_configuration (cfg, my_id);
347 GNUNET_SERVER_disconnect_notify (server,
348 &client_disconnect_notification,
354 * Free the given entry in the blacklist.
357 * @param key host identity (unused)
358 * @param value the blacklist entry
359 * @return #GNUNET_OK (continue to iterate)
362 free_blacklist_entry (void *cls,
363 const struct GNUNET_PeerIdentity *key,
368 GNUNET_free_non_null (be);
374 * Stop blacklist subsystem.
377 GST_blacklist_stop ()
379 if (NULL == blacklist)
381 GNUNET_CONTAINER_multipeermap_iterate (blacklist,
382 &free_blacklist_entry,
384 GNUNET_CONTAINER_multipeermap_destroy (blacklist);
390 * Transmit blacklist query to the client.
392 * @param cls the `struct GST_BlacklistCheck`
393 * @param size number of bytes allowed
394 * @param buf where to copy the message
395 * @return number of bytes copied to @a buf
398 transmit_blacklist_message (void *cls,
402 struct GST_BlacklistCheck *bc = cls;
403 struct Blacklisters *bl;
404 struct BlacklistMessage bm;
409 GNUNET_assert (NULL == bc->task);
410 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
412 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
413 "Failed to send blacklist test for peer `%s' to client\n",
414 GNUNET_i2s (&bc->peer));
417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418 "Sending blacklist test for peer `%s' to client %p\n",
419 GNUNET_i2s (&bc->peer),
422 bm.header.size = htons (sizeof (struct BlacklistMessage));
423 bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
424 bm.is_allowed = htonl (0);
429 if (GNUNET_YES == bl->call_receive_done)
431 GNUNET_SERVER_receive_done (bl->client,
433 bl->call_receive_done = GNUNET_NO;
436 bl->waiting_for_reply = GNUNET_YES;
442 * Perform next action in the blacklist check.
444 * @param cls the `struct GST_BlacklistCheck *`
448 do_blacklist_check (void *cls,
449 const struct GNUNET_SCHEDULER_TaskContext *tc)
451 struct GST_BlacklistCheck *bc = cls;
452 struct Blacklisters *bl;
458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
459 "No other blacklist clients active, will allow neighbour `%s'\n",
460 GNUNET_i2s (&bc->peer));
462 bc->cont (bc->cont_cls,
467 GST_blacklist_test_cancel (bc);
470 if ( (NULL != bl->bc) ||
471 (GNUNET_NO != bl->waiting_for_reply) )
472 return; /* someone else busy with this client */
475 GNUNET_SERVER_notify_transmit_ready (bl->client,
476 sizeof (struct BlacklistMessage),
477 GNUNET_TIME_UNIT_FOREVER_REL,
478 &transmit_blacklist_message,
484 * Got the result about an existing connection from a new blacklister.
485 * Shutdown the neighbour if necessary.
488 * @param peer the neighbour that was investigated
489 * @param address address associated with the request
490 * @param session session associated with the request
491 * @param allowed #GNUNET_OK if we can keep it,
492 * #GNUNET_NO if we must shutdown the connection
495 confirm_or_drop_neighbour (void *cls,
496 const struct GNUNET_PeerIdentity *peer,
497 const struct GNUNET_HELLO_Address *address,
498 struct GNUNET_ATS_Session *session,
501 if (GNUNET_OK == allowed)
502 return; /* we're done */
503 GNUNET_STATISTICS_update (GST_stats,
504 gettext_noop ("# disconnects due to blacklist"),
507 GST_neighbours_force_disconnect (peer);
512 * Closure for #test_connection_ok().
514 struct TestConnectionContext
517 * Is this the first neighbour we're checking?
522 * Handle to the blacklisting client we need to ask.
524 struct Blacklisters *bl;
529 * Test if an existing connection is still acceptable given a new
530 * blacklisting client.
532 * @param cls the `struct TestConnectionContext *`
533 * @param peer identity of the peer
534 * @param address the address
535 * @param state current state this peer is in
536 * @param state_timeout timeout for the current state of the peer
537 * @param bandwidth_in bandwidth assigned inbound
538 * @param bandwidth_out bandwidth assigned outbound
541 test_connection_ok (void *cls,
542 const struct GNUNET_PeerIdentity *peer,
543 const struct GNUNET_HELLO_Address *address,
544 enum GNUNET_TRANSPORT_PeerState state,
545 struct GNUNET_TIME_Absolute state_timeout,
546 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
547 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
549 struct TestConnectionContext *tcc = cls;
550 struct GST_BlacklistCheck *bc;
552 bc = GNUNET_new (struct GST_BlacklistCheck);
553 GNUNET_CONTAINER_DLL_insert (bc_head,
557 bc->address = GNUNET_HELLO_address_copy (address);
558 bc->cont = &confirm_or_drop_neighbour;
560 bc->bl_pos = tcc->bl;
561 if (GNUNET_YES == tcc->first)
563 /* all would wait for the same client, no need to
564 * create more than just the first task right now */
565 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
566 tcc->first = GNUNET_NO;
572 * Initialize a blacklisting client. We got a blacklist-init
573 * message from this client, add him to the list of clients
574 * to query for blacklisting.
577 * @param client the client
578 * @param message the blacklist-init message that was sent
581 GST_blacklist_handle_init (void *cls,
582 struct GNUNET_SERVER_Client *client,
583 const struct GNUNET_MessageHeader *message)
585 struct Blacklisters *bl;
586 struct TestConnectionContext tcc;
588 for (bl = bl_head; NULL != bl; bl = bl->next)
589 if (bl->client == client)
592 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
596 GNUNET_SERVER_client_mark_monitor (client);
597 bl = GNUNET_new (struct Blacklisters);
599 bl->call_receive_done = GNUNET_YES;
600 GNUNET_SERVER_client_keep (client);
601 GNUNET_CONTAINER_DLL_insert_after (bl_head,
605 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606 "New blacklist client %p\n",
609 /* confirm that all existing connections are OK! */
611 tcc.first = GNUNET_YES;
612 GST_neighbours_iterate (&test_connection_ok, &tcc);
617 * A blacklisting client has sent us reply. Process it.
620 * @param client the client
621 * @param message the blacklist-init message that was sent
624 GST_blacklist_handle_reply (void *cls,
625 struct GNUNET_SERVER_Client *client,
626 const struct GNUNET_MessageHeader *message)
628 const struct BlacklistMessage *msg =
629 (const struct BlacklistMessage *) message;
630 struct Blacklisters *bl;
631 struct GST_BlacklistCheck *bc;
634 while ((bl != NULL) && (bl->client != client))
638 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639 "Blacklist client disconnected\n");
640 GNUNET_SERVER_receive_done (client,
645 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
646 "Blacklist client %p sent reply for `%s'\n",
648 GNUNET_i2s (&msg->peer));
652 bl->waiting_for_reply = GNUNET_NO;
653 bl->call_receive_done = GNUNET_YES; /* Remember to call receive_done */
656 /* only run this if the blacklist check has not been
657 * cancelled in the meantime... */
658 GNUNET_assert (bc->bl_pos == bl);
659 if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
661 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
662 "Blacklist check failed, peer not allowed\n");
663 /* For the duration of the continuation, make the ongoing
664 check invisible (to avoid double-cancellation); then
665 add it back again so we can re-use GST_blacklist_test_cancel() */
666 GNUNET_CONTAINER_DLL_remove (bc_head,
669 bc->cont (bc->cont_cls,
674 GNUNET_CONTAINER_DLL_insert (bc_head,
677 GST_blacklist_test_cancel (bc);
678 GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
679 bl->call_receive_done = GNUNET_NO;
684 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685 "Blacklist check succeeded, continuing with checks\n");
686 GNUNET_SERVER_receive_done (bl->client,
688 bl->call_receive_done = GNUNET_NO;
689 bc->bl_pos = bl->next;
690 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
694 /* check if any other blacklist checks are waiting for this blacklister */
695 for (bc = bc_head; bc != NULL; bc = bc->next)
696 if ((bc->bl_pos == bl) && (NULL == bc->task))
698 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
706 * Add the given peer to the blacklist (for the given transport).
708 * @param peer peer to blacklist
709 * @param transport_name transport to blacklist for this peer, NULL for all
712 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
713 const char *transport_name)
715 char *transport = NULL;
717 if (NULL != transport_name)
719 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
720 "Adding peer `%s' with plugin `%s' to blacklist\n",
721 GNUNET_i2s (peer), transport_name);
722 transport = GNUNET_strdup (transport_name);
725 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
726 "Adding peer `%s' with all plugins to blacklist\n",
728 if (NULL == blacklist)
730 GNUNET_CONTAINER_multipeermap_create (TRANSPORT_BLACKLIST_HT_SIZE,
733 GNUNET_CONTAINER_multipeermap_put (blacklist, peer,
735 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
740 * Abort blacklist if @a address and @a session match.
742 * @param address address used to abort matching checks
743 * @param session session used to abort matching checks
746 GST_blacklist_abort_matching (const struct GNUNET_HELLO_Address *address,
747 struct GNUNET_ATS_Session *session)
749 struct GST_BlacklistCheck *bc;
750 struct GST_BlacklistCheck *n;
753 while (NULL != (bc = n))
756 if ( (bc->session == session) &&
757 (0 == GNUNET_HELLO_address_cmp (bc->address,
760 bc->cont (bc->cont_cls,
765 GST_blacklist_test_cancel (bc);
772 * Test if the given blacklist entry matches. If so,
773 * abort the iteration.
775 * @param cls the transport name to match (const char*)
776 * @param key the key (unused)
777 * @param value the 'char *' (name of a blacklisted transport)
778 * @return #GNUNET_OK if the entry does not match, #GNUNET_NO if it matches
781 test_blacklisted (void *cls,
782 const struct GNUNET_PeerIdentity *key,
785 const char *transport_name = cls;
788 /* Blacklist entry be:
789 * (NULL == be): peer is blacklisted with all plugins
790 * (NULL != be): peer is blacklisted for a specific plugin
792 * If (NULL != transport_name) we look for a transport specific entry:
793 * if (transport_name == be) forbidden
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798 "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
800 (NULL == transport_name) ? "unspecified" : transport_name,
801 (NULL == be) ? "all plugins" : be);
802 /* all plugins for this peer were blacklisted: disallow */
806 /* blacklist check for specific transport */
807 if ((NULL != transport_name) && (NULL != value))
809 if (0 == strcmp (transport_name,
811 return GNUNET_NO; /* plugin is blacklisted! */
818 * Test if a peer/transport combination is blacklisted.
820 * @param peer the identity of the peer to test
821 * @param transport_name name of the transport to test, never NULL
822 * @param cont function to call with result
823 * @param cont_cls closure for @a cont
824 * @param address address to pass back to @a cont, can be NULL
825 * @param session session to pass back to @a cont, can be NULL
826 * @return handle to the blacklist check, NULL if the decision
827 * was made instantly and @a cont was already called
829 struct GST_BlacklistCheck *
830 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
831 const char *transport_name,
832 GST_BlacklistTestContinuation cont,
834 const struct GNUNET_HELLO_Address *address,
835 struct GNUNET_ATS_Session *session)
837 struct GST_BlacklistCheck *bc;
839 GNUNET_assert (NULL != peer);
840 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
841 "Blacklist check for peer `%s':%s\n",
843 (NULL != transport_name) ? transport_name : "unspecified");
845 /* Check local blacklist by iterating over hashmap
846 * If iteration is aborted, we found a matching blacklist entry */
847 if ((NULL != blacklist) &&
849 GNUNET_CONTAINER_multipeermap_get_multiple (blacklist, peer,
851 (void *) transport_name)))
853 /* Disallowed by config, disapprove instantly */
854 GNUNET_STATISTICS_update (GST_stats,
855 gettext_noop ("# disconnects due to blacklist"),
858 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
859 _("Disallowing connection to peer `%s' on transport %s\n"),
861 (NULL != transport_name) ? transport_name : "unspecified");
873 /* no blacklist clients, approve instantly */
880 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
881 "Allowing connection to peer `%s' %s\n",
883 (NULL != transport_name) ? transport_name : "");
887 /* need to query blacklist clients */
888 bc = GNUNET_new (struct GST_BlacklistCheck);
889 GNUNET_CONTAINER_DLL_insert (bc_head,
893 bc->address = GNUNET_HELLO_address_copy (address);
894 bc->session = session;
896 bc->cont_cls = cont_cls;
897 bc->bl_pos = bl_head;
898 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
904 * Cancel a blacklist check.
906 * @param bc check to cancel
909 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
911 GNUNET_CONTAINER_DLL_remove (bc_head,
914 if (NULL != bc->bl_pos)
916 if (bc->bl_pos->bc == bc)
918 /* we're at the head of the queue, remove us! */
919 bc->bl_pos->bc = NULL;
922 if (NULL != bc->task)
924 GNUNET_SCHEDULER_cancel (bc->task);
929 GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
932 GNUNET_free_non_null (bc->address);
937 /* end of file gnunet-service-transport_blacklist.c */