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 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.
69 struct BlacklistCheck *bc;
76 * Context we use when performing a blacklist check.
82 * This is a linked list.
84 struct BlacklistCheck *next;
87 * This is a linked list.
89 struct BlacklistCheck *prev;
94 struct GNUNET_PeerIdentity peer;
97 * Continuation to call with the result.
99 GST_BlacklistTestContinuation cont;
107 * Current transmission request handle for this client, or NULL if no
108 * request is pending.
110 struct GNUNET_CONNECTION_TransmitHandle *th;
113 * Our current position in the blacklisters list.
115 struct Blacklisters *bl_pos;
118 * Current task performing the check.
120 GNUNET_SCHEDULER_TaskIdentifier task;
126 * Head of DLL of active blacklisting queries.
128 static struct BlacklistCheck *bc_head;
131 * Tail of DLL of active blacklisting queries.
133 static struct BlacklistCheck *bc_tail;
136 * Head of DLL of blacklisting clients.
138 static struct Blacklisters *bl_head;
141 * Tail of DLL of blacklisting clients.
143 static struct Blacklisters *bl_tail;
146 * Hashmap of blacklisted peers. Values are of type 'char *' (transport names),
147 * can be NULL if we have no static blacklist.
149 static struct GNUNET_CONTAINER_MultiHashMap *blacklist;
153 * Perform next action in the blacklist check.
155 * @param cls the 'struct BlacklistCheck*'
159 do_blacklist_check (void *cls,
160 const struct GNUNET_SCHEDULER_TaskContext *tc);
164 * Called whenever a client is disconnected. Frees our
165 * resources associated with that client.
167 * @param cls closure (unused)
168 * @param client identification of the client
171 client_disconnect_notification (void *cls,
172 struct GNUNET_SERVER_Client *client)
174 struct Blacklisters *bl;
175 struct BlacklistCheck *bc;
179 for (bl = bl_head; bl != NULL; bl = bl->next)
181 if (bl->client != client)
183 for (bc = bc_head; bc != NULL; bc = bc->next)
185 if (bc->bl_pos != bl)
187 bc->bl_pos = bl->next;
190 GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
193 if (bc->task == GNUNET_SCHEDULER_NO_TASK)
194 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
198 GNUNET_CONTAINER_DLL_remove (bl_head,
201 GNUNET_SERVER_client_drop (bl->client);
209 * Read the blacklist file, containing transport:peer entries.
210 * Provided the transport is loaded, set up hashmap with these
211 * entries to blacklist peers by transport.
215 read_blacklist_file ()
222 struct GNUNET_PeerIdentity pid;
224 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
225 unsigned int entries_found;
226 char *transport_name;
229 GNUNET_CONFIGURATION_get_value_filename (GST_cfg,
235 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
236 "Option `%s' in section `%s' not specified!\n",
242 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
243 GNUNET_DISK_fn_write (fn, NULL, 0,
244 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
245 if (0 != STAT (fn, &frstat))
247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
248 _("Could not read blacklist file `%s'\n"),
253 if (frstat.st_size == 0)
256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257 _("Blacklist file `%s' is empty.\n"),
263 /* FIXME: use mmap */
264 data = GNUNET_malloc_large (frstat.st_size);
265 GNUNET_assert(data != NULL);
266 if (frstat.st_size !=
267 GNUNET_DISK_fn_read (fn, data, frstat.st_size))
269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270 _("Failed to read blacklist from `%s'\n"),
278 while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
280 while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
281 (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
284 while ( (colon_pos < frstat.st_size) &&
285 (data[colon_pos] != ':') &&
286 (! isspace ( (unsigned char) data[colon_pos])) )
288 if (colon_pos >= frstat.st_size)
290 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
291 _("Syntax error in blacklist file at offset %llu, giving up!\n"),
292 (unsigned long long) colon_pos);
298 if (isspace( (unsigned char) data[colon_pos]))
300 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
301 _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
302 (unsigned long long) colon_pos);
304 while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
308 tsize = colon_pos - pos;
309 if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) || (tsize == 0))
311 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
312 _("Syntax error in blacklist file at offset %llu, giving up!\n"),
313 (unsigned long long) colon_pos);
322 transport_name = GNUNET_malloc(tsize + 1);
323 memcpy(transport_name, &data[pos], tsize);
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327 "Read transport name `%s' in blacklist file.\n",
332 sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
333 if (! isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
335 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
336 _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
337 (unsigned long long) pos);
339 while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
341 GNUNET_free_non_null(transport_name);
344 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
345 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
347 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
348 _("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
349 (unsigned long long) pos,
354 if (0 != memcmp (&pid,
356 sizeof (struct GNUNET_PeerIdentity)))
359 GST_blacklist_add_peer (&pid,
364 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
365 _("Found myself `%s' in blacklist (useless, ignored)\n"),
369 pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
370 GNUNET_free_non_null(transport_name);
371 while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
374 GNUNET_STATISTICS_update (GST_stats,
375 "# Transport entries blacklisted",
384 * Start blacklist subsystem.
386 * @param server server used to accept clients from
389 GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
391 read_blacklist_file ();
392 GNUNET_SERVER_disconnect_notify (server,
393 &client_disconnect_notification,
399 * Free the given entry in the blacklist.
402 * @param key host identity (unused)
403 * @param value the blacklist entry
404 * @return GNUNET_OK (continue to iterate)
407 free_blacklist_entry (void *cls,
408 const GNUNET_HashCode *key,
419 * Stop blacklist subsystem.
422 GST_blacklist_stop ()
424 if (NULL != blacklist)
426 GNUNET_CONTAINER_multihashmap_iterate (blacklist,
427 &free_blacklist_entry,
429 GNUNET_CONTAINER_multihashmap_destroy (blacklist);
436 * Transmit blacklist query to the client.
438 * @param cls the 'struct BlacklistCheck'
439 * @param size number of bytes allowed
440 * @param buf where to copy the message
441 * @return number of bytes copied to buf
444 transmit_blacklist_message (void *cls,
448 struct BlacklistCheck *bc = cls;
449 struct Blacklisters *bl;
450 struct BlacklistMessage bm;
455 GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
456 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
458 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
459 "Failed to send blacklist test for peer `%s' to client\n",
460 GNUNET_i2s (&bc->peer));
464 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465 "Sending blacklist test for peer `%s' to client\n",
466 GNUNET_i2s (&bc->peer));
469 bm.header.size = htons (sizeof (struct BlacklistMessage));
470 bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
471 bm.is_allowed = htonl (0);
473 memcpy (buf, &bm, sizeof (bm));
474 GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
480 * Perform next action in the blacklist check.
482 * @param cls the 'struct BlacklistCheck*'
486 do_blacklist_check (void *cls,
487 const struct GNUNET_SCHEDULER_TaskContext *tc)
489 struct BlacklistCheck *bc = cls;
490 struct Blacklisters *bl;
492 bc->task = GNUNET_SCHEDULER_NO_TASK;
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498 "No other blacklist clients active, will allow neighbour `%s'\n",
499 GNUNET_i2s (&bc->peer));
501 bc->cont (bc->cont_cls,
508 return; /* someone else busy with this client */
510 bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client,
511 sizeof (struct BlacklistMessage),
512 GNUNET_TIME_UNIT_FOREVER_REL,
513 &transmit_blacklist_message,
519 * Got the result about an existing connection from a new blacklister.
520 * Shutdown the neighbour if necessary.
523 * @param peer the neighbour that was investigated
524 * @param allowed GNUNET_OK if we can keep it,
525 * GNUNET_NO if we must shutdown the connection
528 confirm_or_drop_neighbour (void *cls,
529 const struct GNUNET_PeerIdentity *peer,
532 if (GNUNET_OK == allowed)
533 return; /* we're done */
534 GNUNET_STATISTICS_update (GST_stats,
535 gettext_noop ("# disconnects due to blacklist"),
538 GST_neighbours_force_disconnect (peer);
543 * Closure for 'test_connection_ok'.
545 struct TestConnectionContext
548 * Is this the first neighbour we're checking?
553 * Handle to the blacklisting client we need to ask.
555 struct Blacklisters *bl;
560 * Test if an existing connection is still acceptable given a new
561 * blacklisting client.
563 * @param cls the 'struct TestConnectionContest'
564 * @param pid neighbour's identity
567 test_connection_ok (void *cls,
568 const struct GNUNET_PeerIdentity *neighbour)
570 struct TestConnectionContext *tcc = cls;
571 struct BlacklistCheck *bc;
573 bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
574 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
575 bc->peer = *neighbour;
576 bc->cont = &confirm_or_drop_neighbour;
578 bc->bl_pos = tcc->bl;
579 if (GNUNET_YES == tcc->first)
581 /* all would wait for the same client, no need to
582 create more than just the first task right now */
583 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
585 tcc->first = GNUNET_NO;
592 * Initialize a blacklisting client. We got a blacklist-init
593 * message from this client, add him to the list of clients
594 * to query for blacklisting.
597 * @param client the client
598 * @param message the blacklist-init message that was sent
601 GST_blacklist_handle_init (void *cls,
602 struct GNUNET_SERVER_Client *client,
603 const struct GNUNET_MessageHeader *message)
605 struct Blacklisters *bl;
606 struct TestConnectionContext tcc;
611 if (bl->client == client)
614 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
619 bl = GNUNET_malloc (sizeof (struct Blacklisters));
621 GNUNET_SERVER_client_keep (client);
622 GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
624 /* confirm that all existing connections are OK! */
626 tcc.first = GNUNET_YES;
627 GST_neighbours_iterate (&test_connection_ok,
633 * A blacklisting client has sent us reply. Process it.
636 * @param client the client
637 * @param message the blacklist-init message that was sent
640 GST_blacklist_handle_reply (void *cls,
641 struct GNUNET_SERVER_Client *client,
642 const struct GNUNET_MessageHeader *message)
644 const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
645 struct Blacklisters *bl;
646 struct BlacklistCheck *bc;
649 while ( (bl != NULL) &&
650 (bl->client != client) )
655 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
656 "Blacklist client disconnected\n");
658 /* FIXME: other error handling here!? */
659 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
664 if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
667 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668 "Blacklist check failed, peer not allowed\n");
670 bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
671 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
677 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
678 "Blacklist check succeeded, continuing with checks\n");
680 bc->bl_pos = bc->bl_pos->next;
681 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
684 /* check if any other bc's are waiting for this blacklister */
688 if ( (bc->bl_pos == bl) &&
689 (GNUNET_SCHEDULER_NO_TASK == bc->task) )
690 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
698 * Add the given peer to the blacklist (for the given transport).
700 * @param peer peer to blacklist
701 * @param transport_name transport to blacklist for this peer, NULL for all
704 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
705 const char *transport_name)
708 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709 "Adding peer `%s' with plugin `%s' to blacklist\n",
713 if (blacklist == NULL)
714 blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);
715 GNUNET_CONTAINER_multihashmap_put (blacklist,
717 GNUNET_strdup (transport_name),
718 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
723 * Test if the given blacklist entry matches. If so,
724 * abort the iteration.
726 * @param cls the transport name to match (const char*)
727 * @param key the key (unused)
728 * @param value the 'char *' (name of a blacklisted transport)
729 * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
732 test_blacklisted (void *cls,
733 const GNUNET_HashCode *key,
736 const char *transport_name = cls;
739 if (0 == strcmp (transport_name,
741 return GNUNET_NO; /* abort iteration! */
747 * Test if a peer/transport combination is blacklisted.
749 * @param peer the identity of the peer to test
750 * @param transport_name name of the transport to test, never NULL
751 * @param cont function to call with result
752 * @param cont_cls closure for 'cont'
755 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
756 const char *transport_name,
757 GST_BlacklistTestContinuation cont,
760 struct BlacklistCheck *bc;
762 if ( (blacklist != NULL) &&
764 GNUNET_CONTAINER_multihashmap_get_multiple (blacklist,
767 (void*) transport_name)) )
769 /* disallowed by config, disapprove instantly */
770 GNUNET_STATISTICS_update (GST_stats,
771 gettext_noop ("# disconnects due to blacklist"),
775 cont (cont_cls, peer, GNUNET_NO);
781 /* no blacklist clients, approve instantly */
783 cont (cont_cls, peer, GNUNET_OK);
787 /* need to query blacklist clients */
788 bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
789 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
792 bc->cont_cls = cont_cls;
793 bc->bl_pos = bl_head;
794 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
800 /* end of file gnunet-service-transport_blacklist.c */