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
27 #include "gnunet-service-transport.h"
28 #include "gnunet-service-transport_blacklist.h"
29 #include "gnunet-service-transport_neighbours.h"
30 #include "transport.h"
34 * Size of the blacklist hash map.
36 #define TRANSPORT_BLACKLIST_HT_SIZE 64
40 * Context we use when performing a blacklist check.
42 struct GST_BlacklistCheck;
46 * Information kept for each client registered to perform
52 * This is a linked list.
54 struct Blacklisters *next;
57 * This is a linked list.
59 struct Blacklisters *prev;
62 * Client responsible for this entry.
64 struct GNUNET_SERVER_Client *client;
67 * Blacklist check that we're currently performing (or NULL
68 * if we're performing one that has been cancelled).
70 struct GST_BlacklistCheck *bc;
73 * Set to GNUNET_YES if we're currently waiting for a reply.
75 int waiting_for_reply;
82 * Context we use when performing a blacklist check.
84 struct GST_BlacklistCheck
88 * This is a linked list.
90 struct GST_BlacklistCheck *next;
93 * This is a linked list.
95 struct GST_BlacklistCheck *prev;
100 struct GNUNET_PeerIdentity peer;
103 * Continuation to call with the result.
105 GST_BlacklistTestContinuation cont;
113 * Current transmission request handle for this client, or NULL if no
114 * request is pending.
116 struct GNUNET_SERVER_TransmitHandle *th;
119 * Our current position in the blacklisters list.
121 struct Blacklisters *bl_pos;
124 * Current task performing the check.
126 GNUNET_SCHEDULER_TaskIdentifier task;
132 * Head of DLL of active blacklisting queries.
134 static struct GST_BlacklistCheck *bc_head;
137 * Tail of DLL of active blacklisting queries.
139 static struct GST_BlacklistCheck *bc_tail;
142 * Head of DLL of blacklisting clients.
144 static struct Blacklisters *bl_head;
147 * Tail of DLL of blacklisting clients.
149 static struct Blacklisters *bl_tail;
152 * Hashmap of blacklisted peers. Values are of type 'char *' (transport names),
153 * can be NULL if we have no static blacklist.
155 static struct GNUNET_CONTAINER_MultiHashMap *blacklist;
159 * Perform next action in the blacklist check.
161 * @param cls the 'struct BlacklistCheck*'
165 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
169 * Called whenever a client is disconnected. Frees our
170 * resources associated with that client.
172 * @param cls closure (unused)
173 * @param client identification of the client
176 client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client)
178 struct Blacklisters *bl;
179 struct GST_BlacklistCheck *bc;
183 for (bl = bl_head; bl != NULL; bl = bl->next)
185 if (bl->client != client)
187 for (bc = bc_head; bc != NULL; bc = bc->next)
189 if (bc->bl_pos != bl)
191 bc->bl_pos = bl->next;
194 GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
197 if (bc->task == GNUNET_SCHEDULER_NO_TASK)
198 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
201 GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
202 GNUNET_SERVER_client_drop (bl->client);
210 * Function to iterate over options in the blacklisting section for a peer.
213 * @param section name of the section
214 * @param option name of the option
215 * @param value value of the option
218 blacklist_cfg_iter (void *cls, const char *section,
222 unsigned int *res = cls;
223 struct GNUNET_PeerIdentity peer;
227 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string2 (option,
232 if ((NULL == value) || (0 == strcmp(value, "")))
234 /* Blacklist whole peer */
235 GST_blacklist_add_peer (&peer, NULL);
236 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
237 _("Adding blacklisting entry for peer `%s'\n"), GNUNET_i2s (&peer));
241 plugs = GNUNET_strdup (value);
242 for (pos = strtok (plugs, " "); pos != NULL; pos = strtok (NULL, " "))
244 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
245 _("Adding blacklisting entry for peer `%s':`%s'\n"),
246 GNUNET_i2s (&peer), pos);
247 GST_blacklist_add_peer (&peer, pos);
256 * Read blacklist configuration
258 * @param cfg the configuration handle
259 * @param my_id my peer identity
262 read_blacklist_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg,
263 const struct GNUNET_PeerIdentity *my_id)
266 unsigned int res = 0;
268 GNUNET_snprintf (cfg_sect,
270 "transport-blacklist-%s",
271 GNUNET_i2s_full (my_id));
272 GNUNET_CONFIGURATION_iterate_section_values (cfg, cfg_sect, &blacklist_cfg_iter, &res);
273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274 "Loaded %u blacklisting entries from configuration\n", res);
279 * Start blacklist subsystem.
281 * @param server server used to accept clients from
282 * @param cfg configuration handle
283 * @param my_id my peer id
286 GST_blacklist_start (struct GNUNET_SERVER_Handle *server,
287 const struct GNUNET_CONFIGURATION_Handle *cfg,
288 const struct GNUNET_PeerIdentity *my_id)
290 GNUNET_assert (NULL != cfg);
291 GNUNET_assert (NULL != my_id);
292 read_blacklist_configuration (cfg, my_id);
293 GNUNET_SERVER_disconnect_notify (server, &client_disconnect_notification,
299 * Free the given entry in the blacklist.
302 * @param key host identity (unused)
303 * @param value the blacklist entry
304 * @return GNUNET_OK (continue to iterate)
307 free_blacklist_entry (void *cls, const struct GNUNET_HashCode * key, void *value)
311 GNUNET_free_non_null (be);
317 * Stop blacklist subsystem.
320 GST_blacklist_stop ()
322 if (NULL != blacklist)
324 GNUNET_CONTAINER_multihashmap_iterate (blacklist, &free_blacklist_entry,
326 GNUNET_CONTAINER_multihashmap_destroy (blacklist);
333 * Transmit blacklist query to the client.
335 * @param cls the 'struct GST_BlacklistCheck'
336 * @param size number of bytes allowed
337 * @param buf where to copy the message
338 * @return number of bytes copied to buf
341 transmit_blacklist_message (void *cls, size_t size, void *buf)
343 struct GST_BlacklistCheck *bc = cls;
344 struct Blacklisters *bl;
345 struct BlacklistMessage bm;
350 GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
351 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
352 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
353 "Failed to send blacklist test for peer `%s' to client\n",
354 GNUNET_i2s (&bc->peer));
357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
358 "Sending blacklist test for peer `%s' to client\n",
359 GNUNET_i2s (&bc->peer));
361 bm.header.size = htons (sizeof (struct BlacklistMessage));
362 bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
363 bm.is_allowed = htonl (0);
365 memcpy (buf, &bm, sizeof (bm));
366 GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
367 bl->waiting_for_reply = GNUNET_YES;
373 * Perform next action in the blacklist check.
375 * @param cls the 'struct GST_BlacklistCheck*'
379 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
381 struct GST_BlacklistCheck *bc = cls;
382 struct Blacklisters *bl;
384 bc->task = GNUNET_SCHEDULER_NO_TASK;
388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
389 "No other blacklist clients active, will allow neighbour `%s'\n",
390 GNUNET_i2s (&bc->peer));
391 bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK);
392 GNUNET_CONTAINER_DLL_remove(bc_head, bc_tail, bc);
396 if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO))
397 return; /* someone else busy with this client */
400 GNUNET_SERVER_notify_transmit_ready (bl->client,
401 sizeof (struct BlacklistMessage),
402 GNUNET_TIME_UNIT_FOREVER_REL,
403 &transmit_blacklist_message, bc);
408 * Got the result about an existing connection from a new blacklister.
409 * Shutdown the neighbour if necessary.
412 * @param peer the neighbour that was investigated
413 * @param allowed GNUNET_OK if we can keep it,
414 * GNUNET_NO if we must shutdown the connection
417 confirm_or_drop_neighbour (void *cls, const struct GNUNET_PeerIdentity *peer,
420 if (GNUNET_OK == allowed)
421 return; /* we're done */
422 GNUNET_STATISTICS_update (GST_stats,
423 gettext_noop ("# disconnects due to blacklist"), 1,
425 GST_neighbours_force_disconnect (peer);
430 * Closure for 'test_connection_ok'.
432 struct TestConnectionContext
435 * Is this the first neighbour we're checking?
440 * Handle to the blacklisting client we need to ask.
442 struct Blacklisters *bl;
447 * Test if an existing connection is still acceptable given a new
448 * blacklisting client.
450 * @param cls the 'struct TestConnectionContest'
451 * @param neighbour neighbour's identity
452 * @param address the address
453 * @param bandwidth_in inbound quota in NBO
454 * @param bandwidth_out outbound quota in NBO
457 test_connection_ok (void *cls, const struct GNUNET_PeerIdentity *neighbour,
458 const struct GNUNET_HELLO_Address *address,
459 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
460 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out)
462 struct TestConnectionContext *tcc = cls;
463 struct GST_BlacklistCheck *bc;
465 bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
466 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
467 bc->peer = *neighbour;
468 bc->cont = &confirm_or_drop_neighbour;
470 bc->bl_pos = tcc->bl;
471 if (GNUNET_YES == tcc->first)
473 /* all would wait for the same client, no need to
474 * create more than just the first task right now */
475 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
476 tcc->first = GNUNET_NO;
482 * Initialize a blacklisting client. We got a blacklist-init
483 * message from this client, add him to the list of clients
484 * to query for blacklisting.
487 * @param client the client
488 * @param message the blacklist-init message that was sent
491 GST_blacklist_handle_init (void *cls, struct GNUNET_SERVER_Client *client,
492 const struct GNUNET_MessageHeader *message)
494 struct Blacklisters *bl;
495 struct TestConnectionContext tcc;
500 if (bl->client == client)
503 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
508 GNUNET_SERVER_client_mark_monitor (client);
509 bl = GNUNET_malloc (sizeof (struct Blacklisters));
511 GNUNET_SERVER_client_keep (client);
512 GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
514 /* confirm that all existing connections are OK! */
516 tcc.first = GNUNET_YES;
517 GST_neighbours_iterate (&test_connection_ok, &tcc);
522 * A blacklisting client has sent us reply. Process it.
525 * @param client the client
526 * @param message the blacklist-init message that was sent
529 GST_blacklist_handle_reply (void *cls, struct GNUNET_SERVER_Client *client,
530 const struct GNUNET_MessageHeader *message)
532 const struct BlacklistMessage *msg =
533 (const struct BlacklistMessage *) message;
534 struct Blacklisters *bl;
535 struct GST_BlacklistCheck *bc;
538 while ((bl != NULL) && (bl->client != client))
542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n");
543 /* FIXME: other error handling here!? */
544 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
549 bl->waiting_for_reply = GNUNET_NO;
552 /* only run this if the blacklist check has not been
553 * cancelled in the meantime... */
554 if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
557 "Blacklist check failed, peer not allowed\n");
558 bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
559 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
564 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
565 "Blacklist check succeeded, continuing with checks\n");
566 bc->bl_pos = bc->bl_pos->next;
567 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
570 /* check if any other bc's are waiting for this blacklister */
572 for (bc = bc_head; bc != NULL; bc = bc->next)
573 if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
575 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
582 * Add the given peer to the blacklist (for the given transport).
584 * @param peer peer to blacklist
585 * @param transport_name transport to blacklist for this peer, NULL for all
588 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
589 const char *transport_name)
591 char * transport = NULL;
592 if (NULL != transport_name)
594 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
595 "Adding peer `%s' with plugin `%s' to blacklist\n",
596 GNUNET_i2s (peer), transport_name);
597 transport = GNUNET_strdup (transport_name);
600 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
601 "Adding peer `%s' with all plugins to blacklist\n",
603 if (blacklist == NULL)
605 GNUNET_CONTAINER_multihashmap_create (TRANSPORT_BLACKLIST_HT_SIZE,
608 GNUNET_CONTAINER_multihashmap_put (blacklist, &peer->hashPubKey,
610 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
615 * Test if the given blacklist entry matches. If so,
616 * abort the iteration.
618 * @param cls the transport name to match (const char*)
619 * @param key the key (unused)
620 * @param value the 'char *' (name of a blacklisted transport)
621 * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
624 test_blacklisted (void *cls, const struct GNUNET_HashCode * key, void *value)
626 const char *transport_name = cls;
629 /* Blacklist entry be:
630 * (NULL == be): peer is blacklisted with all plugins
631 * (NULL != be): peer is blacklisted for a specific plugin
633 * If (NULL != transport_name) we look for a transport specific entry:
634 * if (transport_name == be) forbidden
638 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
639 "Comparing BL request for peer `%4s':`%s' with BL entry: `%s'\n",
641 (NULL == transport_name) ? "unspecified" : transport_name,
642 (NULL == be) ? "all plugins" : be);
643 /* all plugins for this peer were blacklisted: disallow */
647 /* blacklist check for specific transport */
648 if ((NULL != transport_name) && (NULL != value))
650 if (0 == strcmp (transport_name, be))
651 return GNUNET_NO; /* plugin is blacklisted! */
658 * Test if a peer/transport combination is blacklisted.
660 * @param peer the identity of the peer to test
661 * @param transport_name name of the transport to test, never NULL
662 * @param cont function to call with result
663 * @param cont_cls closure for 'cont'
664 * @return handle to the blacklist check, NULL if the decision
665 * was made instantly and 'cont' was already called
667 struct GST_BlacklistCheck *
668 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
669 const char *transport_name,
670 GST_BlacklistTestContinuation cont, void *cont_cls)
672 struct GST_BlacklistCheck *bc;
674 GNUNET_assert (peer != NULL);
675 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist check for peer `%s':%s\n",
676 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
678 /* Check local blacklist by iterating over hashmap
679 * If iteration is aborted, we found a matching blacklist entry */
680 if ((blacklist != NULL) &&
682 GNUNET_CONTAINER_multihashmap_get_multiple (blacklist, &peer->hashPubKey,
684 (void *) transport_name)))
686 /* Disallowed by config, disapprove instantly */
687 GNUNET_STATISTICS_update (GST_stats,
688 gettext_noop ("# disconnects due to blacklist"),
690 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disallowing connection to peer `%s' on transport %s\n",
691 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "unspecified");
693 cont (cont_cls, peer, GNUNET_NO);
699 /* no blacklist clients, approve instantly */
701 cont (cont_cls, peer, GNUNET_OK);
702 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Allowing connection to peer `%s' %s\n",
703 GNUNET_i2s (peer), (NULL != transport_name) ? transport_name : "");
707 /* need to query blacklist clients */
708 bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
709 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
712 bc->cont_cls = cont_cls;
713 bc->bl_pos = bl_head;
714 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
720 * Cancel a blacklist check.
722 * @param bc check to cancel
725 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
727 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
728 if (bc->bl_pos != NULL)
730 if (bc->bl_pos->bc == bc)
732 /* we're at the head of the queue, remove us! */
733 bc->bl_pos->bc = NULL;
736 if (GNUNET_SCHEDULER_NO_TASK != bc->task)
738 GNUNET_SCHEDULER_cancel (bc->task);
739 bc->task = GNUNET_SCHEDULER_NO_TASK;
743 GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
750 /* end of file gnunet-service-transport_blacklist.c */