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
565 * @param ats performance data
566 * @param ats_count number of entries in ats (excluding 0-termination)
569 test_connection_ok (void *cls,
570 const struct GNUNET_PeerIdentity *neighbour,
571 const struct GNUNET_TRANSPORT_ATS_Information *ats,
574 struct TestConnectionContext *tcc = cls;
575 struct BlacklistCheck *bc;
577 bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
578 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
579 bc->peer = *neighbour;
580 bc->cont = &confirm_or_drop_neighbour;
582 bc->bl_pos = tcc->bl;
583 if (GNUNET_YES == tcc->first)
585 /* all would wait for the same client, no need to
586 create more than just the first task right now */
587 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
589 tcc->first = GNUNET_NO;
596 * Initialize a blacklisting client. We got a blacklist-init
597 * message from this client, add him to the list of clients
598 * to query for blacklisting.
601 * @param client the client
602 * @param message the blacklist-init message that was sent
605 GST_blacklist_handle_init (void *cls,
606 struct GNUNET_SERVER_Client *client,
607 const struct GNUNET_MessageHeader *message)
609 struct Blacklisters *bl;
610 struct TestConnectionContext tcc;
615 if (bl->client == client)
618 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
623 bl = GNUNET_malloc (sizeof (struct Blacklisters));
625 GNUNET_SERVER_client_keep (client);
626 GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
628 /* confirm that all existing connections are OK! */
630 tcc.first = GNUNET_YES;
631 GST_neighbours_iterate (&test_connection_ok,
637 * A blacklisting client has sent us reply. Process it.
640 * @param client the client
641 * @param message the blacklist-init message that was sent
644 GST_blacklist_handle_reply (void *cls,
645 struct GNUNET_SERVER_Client *client,
646 const struct GNUNET_MessageHeader *message)
648 const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
649 struct Blacklisters *bl;
650 struct BlacklistCheck *bc;
653 while ( (bl != NULL) &&
654 (bl->client != client) )
659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660 "Blacklist client disconnected\n");
662 /* FIXME: other error handling here!? */
663 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
668 if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
671 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
672 "Blacklist check failed, peer not allowed\n");
674 bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
675 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682 "Blacklist check succeeded, continuing with checks\n");
684 bc->bl_pos = bc->bl_pos->next;
685 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
688 /* check if any other bc's are waiting for this blacklister */
692 if ( (bc->bl_pos == bl) &&
693 (GNUNET_SCHEDULER_NO_TASK == bc->task) )
694 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
702 * Add the given peer to the blacklist (for the given transport).
704 * @param peer peer to blacklist
705 * @param transport_name transport to blacklist for this peer, NULL for all
708 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
709 const char *transport_name)
712 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713 "Adding peer `%s' with plugin `%s' to blacklist\n",
717 if (blacklist == NULL)
718 blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);
719 GNUNET_CONTAINER_multihashmap_put (blacklist,
721 GNUNET_strdup (transport_name),
722 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
727 * Test if the given blacklist entry matches. If so,
728 * abort the iteration.
730 * @param cls the transport name to match (const char*)
731 * @param key the key (unused)
732 * @param value the 'char *' (name of a blacklisted transport)
733 * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
736 test_blacklisted (void *cls,
737 const GNUNET_HashCode *key,
740 const char *transport_name = cls;
743 if (0 == strcmp (transport_name,
745 return GNUNET_NO; /* abort iteration! */
751 * Test if a peer/transport combination is blacklisted.
753 * @param peer the identity of the peer to test
754 * @param transport_name name of the transport to test, never NULL
755 * @param cont function to call with result
756 * @param cont_cls closure for 'cont'
759 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
760 const char *transport_name,
761 GST_BlacklistTestContinuation cont,
764 struct BlacklistCheck *bc;
766 if ( (blacklist != NULL) &&
768 GNUNET_CONTAINER_multihashmap_get_multiple (blacklist,
771 (void*) transport_name)) )
773 /* disallowed by config, disapprove instantly */
774 GNUNET_STATISTICS_update (GST_stats,
775 gettext_noop ("# disconnects due to blacklist"),
779 cont (cont_cls, peer, GNUNET_NO);
785 /* no blacklist clients, approve instantly */
787 cont (cont_cls, peer, GNUNET_OK);
791 /* need to query blacklist clients */
792 bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
793 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
796 bc->cont_cls = cont_cls;
797 bc->bl_pos = bl_head;
798 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
804 /* end of file gnunet-service-transport_blacklist.c */