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_CONNECTION_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,
166 const struct GNUNET_SCHEDULER_TaskContext *tc);
170 * Called whenever a client is disconnected. Frees our
171 * resources associated with that client.
173 * @param cls closure (unused)
174 * @param client identification of the client
177 client_disconnect_notification (void *cls,
178 struct GNUNET_SERVER_Client *client)
180 struct Blacklisters *bl;
181 struct GST_BlacklistCheck *bc;
185 for (bl = bl_head; bl != NULL; bl = bl->next)
187 if (bl->client != client)
189 for (bc = bc_head; bc != NULL; bc = bc->next)
191 if (bc->bl_pos != bl)
193 bc->bl_pos = bl->next;
196 GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
199 if (bc->task == GNUNET_SCHEDULER_NO_TASK)
200 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
204 GNUNET_CONTAINER_DLL_remove (bl_head,
207 GNUNET_SERVER_client_drop (bl->client);
215 * Read the blacklist file, containing transport:peer entries.
216 * Provided the transport is loaded, set up hashmap with these
217 * entries to blacklist peers by transport.
221 read_blacklist_file ()
228 struct GNUNET_PeerIdentity pid;
230 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
231 unsigned int entries_found;
232 char *transport_name;
235 GNUNET_CONFIGURATION_get_value_filename (GST_cfg,
241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242 "Option `%s' in section `%s' not specified!\n",
248 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
249 GNUNET_DISK_fn_write (fn, NULL, 0,
250 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
251 if (0 != STAT (fn, &frstat))
253 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
254 _("Could not read blacklist file `%s'\n"),
259 if (frstat.st_size == 0)
262 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263 _("Blacklist file `%s' is empty.\n"),
269 /* FIXME: use mmap */
270 data = GNUNET_malloc_large (frstat.st_size);
271 GNUNET_assert(data != NULL);
272 if (frstat.st_size !=
273 GNUNET_DISK_fn_read (fn, data, frstat.st_size))
275 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
276 _("Failed to read blacklist from `%s'\n"),
284 while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
286 while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
287 (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
290 while ( (colon_pos < frstat.st_size) &&
291 (data[colon_pos] != ':') &&
292 (! isspace ( (unsigned char) data[colon_pos])) )
294 if (colon_pos >= frstat.st_size)
296 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
297 _("Syntax error in blacklist file at offset %llu, giving up!\n"),
298 (unsigned long long) colon_pos);
304 if (isspace( (unsigned char) data[colon_pos]))
306 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
307 _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
308 (unsigned long long) colon_pos);
310 while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
314 tsize = colon_pos - pos;
315 if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) || (tsize == 0))
317 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
318 _("Syntax error in blacklist file at offset %llu, giving up!\n"),
319 (unsigned long long) colon_pos);
328 transport_name = GNUNET_malloc(tsize + 1);
329 memcpy(transport_name, &data[pos], tsize);
332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333 "Read transport name `%s' in blacklist file.\n",
338 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
339 if (! isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
341 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
342 _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
343 (unsigned long long) pos);
345 while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
347 GNUNET_free_non_null(transport_name);
350 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
351 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
353 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
354 _("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
355 (unsigned long long) pos,
360 if (0 != memcmp (&pid,
362 sizeof (struct GNUNET_PeerIdentity)))
365 GST_blacklist_add_peer (&pid,
370 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
371 _("Found myself `%s' in blacklist (useless, ignored)\n"),
375 pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
376 GNUNET_free_non_null(transport_name);
377 while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
380 GNUNET_STATISTICS_update (GST_stats,
381 "# Transport entries blacklisted",
390 * Start blacklist subsystem.
392 * @param server server used to accept clients from
395 GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
397 read_blacklist_file ();
398 GNUNET_SERVER_disconnect_notify (server,
399 &client_disconnect_notification,
405 * Free the given entry in the blacklist.
408 * @param key host identity (unused)
409 * @param value the blacklist entry
410 * @return GNUNET_OK (continue to iterate)
413 free_blacklist_entry (void *cls,
414 const GNUNET_HashCode *key,
425 * Stop blacklist subsystem.
428 GST_blacklist_stop ()
430 if (NULL != blacklist)
432 GNUNET_CONTAINER_multihashmap_iterate (blacklist,
433 &free_blacklist_entry,
435 GNUNET_CONTAINER_multihashmap_destroy (blacklist);
442 * Transmit blacklist query to the client.
444 * @param cls the 'struct GST_BlacklistCheck'
445 * @param size number of bytes allowed
446 * @param buf where to copy the message
447 * @return number of bytes copied to buf
450 transmit_blacklist_message (void *cls,
454 struct GST_BlacklistCheck *bc = cls;
455 struct Blacklisters *bl;
456 struct BlacklistMessage bm;
461 GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
462 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
464 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
465 "Failed to send blacklist test for peer `%s' to client\n",
466 GNUNET_i2s (&bc->peer));
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Sending blacklist test for peer `%s' to client\n",
472 GNUNET_i2s (&bc->peer));
475 bm.header.size = htons (sizeof (struct BlacklistMessage));
476 bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
477 bm.is_allowed = htonl (0);
479 memcpy (buf, &bm, sizeof (bm));
480 GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
481 bl->waiting_for_reply = GNUNET_YES;
487 * Perform next action in the blacklist check.
489 * @param cls the 'struct GST_BlacklistCheck*'
493 do_blacklist_check (void *cls,
494 const struct GNUNET_SCHEDULER_TaskContext *tc)
496 struct GST_BlacklistCheck *bc = cls;
497 struct Blacklisters *bl;
499 bc->task = GNUNET_SCHEDULER_NO_TASK;
504 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
505 "No other blacklist clients active, will allow neighbour `%s'\n",
506 GNUNET_i2s (&bc->peer));
508 bc->cont (bc->cont_cls,
514 if ( (bl->bc != NULL) ||
515 (bl->waiting_for_reply != GNUNET_NO) )
516 return; /* someone else busy with this client */
518 bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client,
519 sizeof (struct BlacklistMessage),
520 GNUNET_TIME_UNIT_FOREVER_REL,
521 &transmit_blacklist_message,
527 * Got the result about an existing connection from a new blacklister.
528 * Shutdown the neighbour if necessary.
531 * @param peer the neighbour that was investigated
532 * @param allowed GNUNET_OK if we can keep it,
533 * GNUNET_NO if we must shutdown the connection
536 confirm_or_drop_neighbour (void *cls,
537 const struct GNUNET_PeerIdentity *peer,
540 if (GNUNET_OK == allowed)
541 return; /* we're done */
542 GNUNET_STATISTICS_update (GST_stats,
543 gettext_noop ("# disconnects due to blacklist"),
546 GST_neighbours_force_disconnect (peer);
551 * Closure for 'test_connection_ok'.
553 struct TestConnectionContext
556 * Is this the first neighbour we're checking?
561 * Handle to the blacklisting client we need to ask.
563 struct Blacklisters *bl;
568 * Test if an existing connection is still acceptable given a new
569 * blacklisting client.
571 * @param cls the 'struct TestConnectionContest'
572 * @param pid neighbour's identity
573 * @param ats performance data
574 * @param ats_count number of entries in ats (excluding 0-termination)
577 test_connection_ok (void *cls,
578 const struct GNUNET_PeerIdentity *neighbour,
579 const struct GNUNET_TRANSPORT_ATS_Information *ats,
582 struct TestConnectionContext *tcc = cls;
583 struct GST_BlacklistCheck *bc;
585 bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
586 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
587 bc->peer = *neighbour;
588 bc->cont = &confirm_or_drop_neighbour;
590 bc->bl_pos = tcc->bl;
591 if (GNUNET_YES == tcc->first)
593 /* all would wait for the same client, no need to
594 create more than just the first task right now */
595 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
597 tcc->first = GNUNET_NO;
604 * Initialize a blacklisting client. We got a blacklist-init
605 * message from this client, add him to the list of clients
606 * to query for blacklisting.
609 * @param client the client
610 * @param message the blacklist-init message that was sent
613 GST_blacklist_handle_init (void *cls,
614 struct GNUNET_SERVER_Client *client,
615 const struct GNUNET_MessageHeader *message)
617 struct Blacklisters *bl;
618 struct TestConnectionContext tcc;
623 if (bl->client == client)
626 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
631 bl = GNUNET_malloc (sizeof (struct Blacklisters));
633 GNUNET_SERVER_client_keep (client);
634 GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
636 /* confirm that all existing connections are OK! */
638 tcc.first = GNUNET_YES;
639 GST_neighbours_iterate (&test_connection_ok,
645 * A blacklisting client has sent us reply. Process it.
648 * @param client the client
649 * @param message the blacklist-init message that was sent
652 GST_blacklist_handle_reply (void *cls,
653 struct GNUNET_SERVER_Client *client,
654 const struct GNUNET_MessageHeader *message)
656 const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
657 struct Blacklisters *bl;
658 struct GST_BlacklistCheck *bc;
661 while ( (bl != NULL) &&
662 (bl->client != client) )
667 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668 "Blacklist client disconnected\n");
670 /* FIXME: other error handling here!? */
671 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
676 bl->waiting_for_reply = GNUNET_NO;
679 /* only run this if the blacklist check has not been
680 cancelled in the meantime... */
681 if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
684 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685 "Blacklist check failed, peer not allowed\n");
687 bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
688 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
694 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
695 "Blacklist check succeeded, continuing with checks\n");
697 bc->bl_pos = bc->bl_pos->next;
698 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
702 /* check if any other bc's are waiting for this blacklister */
704 for (bc = bc_head; bc != NULL; bc = bc->next)
705 if ( (bc->bl_pos == bl) &&
706 (GNUNET_SCHEDULER_NO_TASK == bc->task) )
708 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
716 * Add the given peer to the blacklist (for the given transport).
718 * @param peer peer to blacklist
719 * @param transport_name transport to blacklist for this peer, NULL for all
722 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
723 const char *transport_name)
726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727 "Adding peer `%s' with plugin `%s' to blacklist\n",
731 if (blacklist == NULL)
732 blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);
733 GNUNET_CONTAINER_multihashmap_put (blacklist,
735 GNUNET_strdup (transport_name),
736 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
741 * Test if the given blacklist entry matches. If so,
742 * abort the iteration.
744 * @param cls the transport name to match (const char*)
745 * @param key the key (unused)
746 * @param value the 'char *' (name of a blacklisted transport)
747 * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
750 test_blacklisted (void *cls,
751 const GNUNET_HashCode *key,
754 const char *transport_name = cls;
757 if (0 == strcmp (transport_name,
759 return GNUNET_NO; /* abort iteration! */
765 * Test if a peer/transport combination is blacklisted.
767 * @param peer the identity of the peer to test
768 * @param transport_name name of the transport to test, never NULL
769 * @param cont function to call with result
770 * @param cont_cls closure for 'cont'
771 * @return handle to the blacklist check
773 struct GST_BlacklistCheck *
774 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
775 const char *transport_name,
776 GST_BlacklistTestContinuation cont,
779 struct GST_BlacklistCheck *bc;
781 if ( (blacklist != NULL) &&
783 GNUNET_CONTAINER_multihashmap_get_multiple (blacklist,
786 (void*) transport_name)) )
788 /* disallowed by config, disapprove instantly */
789 GNUNET_STATISTICS_update (GST_stats,
790 gettext_noop ("# disconnects due to blacklist"),
794 cont (cont_cls, peer, GNUNET_NO);
800 /* no blacklist clients, approve instantly */
802 cont (cont_cls, peer, GNUNET_OK);
806 /* need to query blacklist clients */
807 bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
808 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
811 bc->cont_cls = cont_cls;
812 bc->bl_pos = bl_head;
813 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
820 * Cancel a blacklist check.
822 * @param bc check to cancel
825 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
827 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
828 if (bc->bl_pos != NULL)
830 if (bc->bl_pos->bc == bc)
832 /* we're at the head of the queue, remove us! */
833 bc->bl_pos->bc = NULL;
836 if (GNUNET_SCHEDULER_NO_TASK != bc->task)
838 GNUNET_SCHEDULER_cancel (bc->task);
839 bc->task = GNUNET_SCHEDULER_NO_TASK;
843 GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
850 /* end of file gnunet-service-transport_blacklist.c */