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 * Read the blacklist file, containing transport:peer entries.
211 * Provided the transport is loaded, set up hashmap with these
212 * entries to blacklist peers by transport.
216 read_blacklist_file ()
223 struct GNUNET_PeerIdentity pid;
225 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
226 unsigned int entries_found;
227 char *transport_name;
230 GNUNET_CONFIGURATION_get_value_filename (GST_cfg, "TRANSPORT",
231 "BLACKLIST_FILE", &fn))
234 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
235 "Option `%s' in section `%s' not specified!\n",
236 "BLACKLIST_FILE", "TRANSPORT");
240 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
241 GNUNET_DISK_fn_write (fn, NULL, 0,
242 GNUNET_DISK_PERM_USER_READ |
243 GNUNET_DISK_PERM_USER_WRITE);
244 if (GNUNET_OK != GNUNET_DISK_file_size (fn,
245 &fsize, GNUNET_NO, GNUNET_YES))
247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
248 _("Could not read blacklist file `%s'\n"), fn);
255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Blacklist file `%s' is empty.\n"),
261 /* FIXME: use mmap */
262 data = GNUNET_malloc_large (fsize);
263 GNUNET_assert (data != NULL);
264 if (fsize != GNUNET_DISK_fn_read (fn, data, fsize))
266 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
267 _("Failed to read blacklist from `%s'\n"), fn);
274 while ((pos < fsize) && isspace ((unsigned char) data[pos]))
276 while ((fsize >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
278 fsize - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
281 while ((colon_pos < fsize) && (data[colon_pos] != ':') &&
282 (!isspace ((unsigned char) data[colon_pos])))
284 if (colon_pos >= fsize)
286 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
288 ("Syntax error in blacklist file at offset %llu, giving up!\n"),
289 (unsigned long long) colon_pos);
295 if (isspace ((unsigned char) data[colon_pos]))
297 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
299 ("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
300 (unsigned long long) colon_pos);
302 while ((pos < fsize) && isspace ((unsigned char) data[pos]))
306 tsize = colon_pos - pos;
307 if ((pos >= fsize) || (pos + tsize >= fsize) ||
310 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",
330 memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
333 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
335 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
337 ("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
338 (unsigned long long) pos);
340 while ((pos < fsize) && (!isspace ((unsigned char) data[pos])))
342 GNUNET_free_non_null (transport_name);
345 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
347 GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
349 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
351 ("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
352 (unsigned long long) pos, &enc);
357 memcmp (&pid, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
360 GST_blacklist_add_peer (&pid, transport_name);
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 < fsize) && isspace ((unsigned char) data[pos]))
374 GNUNET_STATISTICS_update (GST_stats, "# Transport entries blacklisted",
375 entries_found, GNUNET_NO);
382 * Start blacklist subsystem.
384 * @param server server used to accept clients from
387 GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
389 read_blacklist_file ();
390 GNUNET_SERVER_disconnect_notify (server, &client_disconnect_notification,
396 * Free the given entry in the blacklist.
399 * @param key host identity (unused)
400 * @param value the blacklist entry
401 * @return GNUNET_OK (continue to iterate)
404 free_blacklist_entry (void *cls, const GNUNET_HashCode * key, void *value)
414 * Stop blacklist subsystem.
417 GST_blacklist_stop ()
419 if (NULL != blacklist)
421 GNUNET_CONTAINER_multihashmap_iterate (blacklist, &free_blacklist_entry,
423 GNUNET_CONTAINER_multihashmap_destroy (blacklist);
430 * Transmit blacklist query to the client.
432 * @param cls the 'struct GST_BlacklistCheck'
433 * @param size number of bytes allowed
434 * @param buf where to copy the message
435 * @return number of bytes copied to buf
438 transmit_blacklist_message (void *cls, size_t size, void *buf)
440 struct GST_BlacklistCheck *bc = cls;
441 struct Blacklisters *bl;
442 struct BlacklistMessage bm;
447 GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
448 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
449 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
450 "Failed to send blacklist test for peer `%s' to client\n",
451 GNUNET_i2s (&bc->peer));
455 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
456 "Sending blacklist test for peer `%s' to client\n",
457 GNUNET_i2s (&bc->peer));
460 bm.header.size = htons (sizeof (struct BlacklistMessage));
461 bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
462 bm.is_allowed = htonl (0);
464 memcpy (buf, &bm, sizeof (bm));
465 GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
466 bl->waiting_for_reply = GNUNET_YES;
472 * Perform next action in the blacklist check.
474 * @param cls the 'struct GST_BlacklistCheck*'
478 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
480 struct GST_BlacklistCheck *bc = cls;
481 struct Blacklisters *bl;
483 bc->task = GNUNET_SCHEDULER_NO_TASK;
488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489 "No other blacklist clients active, will allow neighbour `%s'\n",
490 GNUNET_i2s (&bc->peer));
492 bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK);
493 GNUNET_CONTAINER_DLL_remove(bc_head, bc_tail, bc);
497 if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO))
498 return; /* someone else busy with this client */
501 GNUNET_SERVER_notify_transmit_ready (bl->client,
502 sizeof (struct BlacklistMessage),
503 GNUNET_TIME_UNIT_FOREVER_REL,
504 &transmit_blacklist_message, bc);
509 * Got the result about an existing connection from a new blacklister.
510 * Shutdown the neighbour if necessary.
513 * @param peer the neighbour that was investigated
514 * @param allowed GNUNET_OK if we can keep it,
515 * GNUNET_NO if we must shutdown the connection
518 confirm_or_drop_neighbour (void *cls, const struct GNUNET_PeerIdentity *peer,
521 if (GNUNET_OK == allowed)
522 return; /* we're done */
523 GNUNET_STATISTICS_update (GST_stats,
524 gettext_noop ("# disconnects due to blacklist"), 1,
526 GST_neighbours_force_disconnect (peer);
531 * Closure for 'test_connection_ok'.
533 struct TestConnectionContext
536 * Is this the first neighbour we're checking?
541 * Handle to the blacklisting client we need to ask.
543 struct Blacklisters *bl;
548 * Test if an existing connection is still acceptable given a new
549 * blacklisting client.
551 * @param cls the 'struct TestConnectionContest'
552 * @param neighbour neighbour's identity
553 * @param ats performance data
554 * @param ats_count number of entries in ats (excluding 0-termination)
555 * @param address the address
558 test_connection_ok (void *cls, const struct GNUNET_PeerIdentity *neighbour,
559 const struct GNUNET_ATS_Information *ats,
561 const struct GNUNET_HELLO_Address *address)
563 struct TestConnectionContext *tcc = cls;
564 struct GST_BlacklistCheck *bc;
566 bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
567 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
568 bc->peer = *neighbour;
569 bc->cont = &confirm_or_drop_neighbour;
571 bc->bl_pos = tcc->bl;
572 if (GNUNET_YES == tcc->first)
574 /* all would wait for the same client, no need to
575 * create more than just the first task right now */
576 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
577 tcc->first = GNUNET_NO;
584 * Initialize a blacklisting client. We got a blacklist-init
585 * message from this client, add him to the list of clients
586 * to query for blacklisting.
589 * @param client the client
590 * @param message the blacklist-init message that was sent
593 GST_blacklist_handle_init (void *cls, struct GNUNET_SERVER_Client *client,
594 const struct GNUNET_MessageHeader *message)
596 struct Blacklisters *bl;
597 struct TestConnectionContext tcc;
602 if (bl->client == client)
605 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
610 bl = GNUNET_malloc (sizeof (struct Blacklisters));
612 GNUNET_SERVER_client_keep (client);
613 GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
615 /* confirm that all existing connections are OK! */
617 tcc.first = GNUNET_YES;
618 GST_neighbours_iterate (&test_connection_ok, &tcc);
623 * A blacklisting client has sent us reply. Process it.
626 * @param client the client
627 * @param message the blacklist-init message that was sent
630 GST_blacklist_handle_reply (void *cls, struct GNUNET_SERVER_Client *client,
631 const struct GNUNET_MessageHeader *message)
633 const struct BlacklistMessage *msg =
634 (const struct BlacklistMessage *) message;
635 struct Blacklisters *bl;
636 struct GST_BlacklistCheck *bc;
639 while ((bl != NULL) && (bl->client != client))
644 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n");
646 /* FIXME: other error handling here!? */
647 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
652 bl->waiting_for_reply = GNUNET_NO;
655 /* only run this if the blacklist check has not been
656 * cancelled in the meantime... */
657 if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661 "Blacklist check failed, peer not allowed\n");
663 bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
664 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
670 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
671 "Blacklist check succeeded, continuing with checks\n");
673 bc->bl_pos = bc->bl_pos->next;
674 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
677 /* check if any other bc's are waiting for this blacklister */
679 for (bc = bc_head; bc != NULL; bc = bc->next)
680 if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
682 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
689 * Add the given peer to the blacklist (for the given transport).
691 * @param peer peer to blacklist
692 * @param transport_name transport to blacklist for this peer, NULL for all
695 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
696 const char *transport_name)
699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700 "Adding peer `%s' with plugin `%s' to blacklist\n",
701 GNUNET_i2s (peer), transport_name);
703 if (blacklist == NULL)
705 GNUNET_CONTAINER_multihashmap_create (TRANSPORT_BLACKLIST_HT_SIZE);
706 GNUNET_CONTAINER_multihashmap_put (blacklist, &peer->hashPubKey,
707 GNUNET_strdup (transport_name),
708 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
713 * Test if the given blacklist entry matches. If so,
714 * abort the iteration.
716 * @param cls the transport name to match (const char*)
717 * @param key the key (unused)
718 * @param value the 'char *' (name of a blacklisted transport)
719 * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
722 test_blacklisted (void *cls, const GNUNET_HashCode * key, void *value)
724 const char *transport_name = cls;
727 /* blacklist check for specific no specific transport*/
728 if (transport_name == NULL)
731 /* blacklist check for specific transport */
732 if (0 == strcmp (transport_name, be))
733 return GNUNET_NO; /* abort iteration! */
739 * Test if a peer/transport combination is blacklisted.
741 * @param peer the identity of the peer to test
742 * @param transport_name name of the transport to test, never NULL
743 * @param cont function to call with result
744 * @param cont_cls closure for 'cont'
745 * @return handle to the blacklist check, NULL if the decision
746 * was made instantly and 'cont' was already called
748 struct GST_BlacklistCheck *
749 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
750 const char *transport_name,
751 GST_BlacklistTestContinuation cont, void *cont_cls)
753 struct GST_BlacklistCheck *bc;
755 GNUNET_assert (peer != NULL);
757 if ((blacklist != NULL) &&
759 GNUNET_CONTAINER_multihashmap_get_multiple (blacklist, &peer->hashPubKey,
761 (void *) transport_name)))
763 /* disallowed by config, disapprove instantly */
764 GNUNET_STATISTICS_update (GST_stats,
765 gettext_noop ("# disconnects due to blacklist"),
768 cont (cont_cls, peer, GNUNET_NO);
774 /* no blacklist clients, approve instantly */
776 cont (cont_cls, peer, GNUNET_OK);
780 /* need to query blacklist clients */
781 bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
782 GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
785 bc->cont_cls = cont_cls;
786 bc->bl_pos = bl_head;
787 bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
793 * Cancel a blacklist check.
795 * @param bc check to cancel
798 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
800 GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
801 if (bc->bl_pos != NULL)
803 if (bc->bl_pos->bc == bc)
805 /* we're at the head of the queue, remove us! */
806 bc->bl_pos->bc = NULL;
809 if (GNUNET_SCHEDULER_NO_TASK != bc->task)
811 GNUNET_SCHEDULER_cancel (bc->task);
812 bc->task = GNUNET_SCHEDULER_NO_TASK;
816 GNUNET_SERVER_notify_transmit_ready_cancel (bc->th);
823 /* end of file gnunet-service-transport_blacklist.c */