X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fgnunet-service-transport_blacklist.c;h=7720e467fac2401f7a448418316dc3a6a4e26668;hb=502af2167f7c218366666ca4944bd7cc54b5b19a;hp=10eda72b0b1d1effdd9e22d8b6a437b9df2b09f2;hpb=a476f6ebdf69cd7b365ad7f079dcf0b8361a55cc;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-service-transport_blacklist.c b/src/transport/gnunet-service-transport_blacklist.c index 10eda72b0..7720e467f 100644 --- a/src/transport/gnunet-service-transport_blacklist.c +++ b/src/transport/gnunet-service-transport_blacklist.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2010 Christian Grothoff (and other contributing authors) + (C) 2010,2011 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -20,157 +20,801 @@ /** * @file transport/gnunet-service-transport_blacklist.c - * @brief low-level P2P messaging + * @brief blacklisting implementation * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_protocols.h" -#include "gnunet_util_lib.h" -#include "gnunet_service_lib.h" -#include "transport.h" +#include "gnunet-service-transport.h" #include "gnunet-service-transport_blacklist.h" +#include "gnunet-service-transport_neighbours.h" +#include "transport.h" + + +/** + * Size of the blacklist hash map. + */ +#define TRANSPORT_BLACKLIST_HT_SIZE 64 + + +/** + * Context we use when performing a blacklist check. + */ +struct GST_BlacklistCheck; + + +/** + * Information kept for each client registered to perform + * blacklisting. + */ +struct Blacklisters +{ + /** + * This is a linked list. + */ + struct Blacklisters *next; + + /** + * This is a linked list. + */ + struct Blacklisters *prev; + + /** + * Client responsible for this entry. + */ + struct GNUNET_SERVER_Client *client; + + /** + * Blacklist check that we're currently performing (or NULL + * if we're performing one that has been cancelled). + */ + struct GST_BlacklistCheck *bc; + + /** + * Set to GNUNET_YES if we're currently waiting for a reply. + */ + int waiting_for_reply; + +}; + + + +/** + * Context we use when performing a blacklist check. + */ +struct GST_BlacklistCheck +{ + + /** + * This is a linked list. + */ + struct GST_BlacklistCheck *next; + + /** + * This is a linked list. + */ + struct GST_BlacklistCheck *prev; + + /** + * Peer being checked. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Continuation to call with the result. + */ + GST_BlacklistTestContinuation cont; + + /** + * Closure for cont. + */ + void *cont_cls; + + /** + * Current transmission request handle for this client, or NULL if no + * request is pending. + */ + struct GNUNET_CONNECTION_TransmitHandle *th; + + /** + * Our current position in the blacklisters list. + */ + struct Blacklisters *bl_pos; + + /** + * Current task performing the check. + */ + GNUNET_SCHEDULER_TaskIdentifier task; + +}; + + +/** + * Head of DLL of active blacklisting queries. + */ +static struct GST_BlacklistCheck *bc_head; + +/** + * Tail of DLL of active blacklisting queries. + */ +static struct GST_BlacklistCheck *bc_tail; + +/** + * Head of DLL of blacklisting clients. + */ +static struct Blacklisters *bl_head; + +/** + * Tail of DLL of blacklisting clients. + */ +static struct Blacklisters *bl_tail; + +/** + * Hashmap of blacklisted peers. Values are of type 'char *' (transport names), + * can be NULL if we have no static blacklist. + */ +static struct GNUNET_CONTAINER_MultiHashMap *blacklist; + + +/** + * Perform next action in the blacklist check. + * + * @param cls the 'struct BlacklistCheck*' + * @param tc unused + */ +static void +do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + /** - * Handle a request to start a blacklist. + * Called whenever a client is disconnected. Frees our + * resources associated with that client. * - * @param cls closure (always NULL) + * @param cls closure (unused) * @param client identification of the client - * @param message the actual message + */ +static void +client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct Blacklisters *bl; + struct GST_BlacklistCheck *bc; + + if (client == NULL) + return; + for (bl = bl_head; bl != NULL; bl = bl->next) + { + if (bl->client != client) + continue; + for (bc = bc_head; bc != NULL; bc = bc->next) + { + if (bc->bl_pos != bl) + continue; + bc->bl_pos = bl->next; + if (bc->th != NULL) + { + GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th); + bc->th = NULL; + } + if (bc->task == GNUNET_SCHEDULER_NO_TASK) + bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc); + break; + } + GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl); + GNUNET_SERVER_client_drop (bl->client); + GNUNET_free (bl); + break; + } +} + + +/** + * Read the blacklist file, containing transport:peer entries. + * Provided the transport is loaded, set up hashmap with these + * entries to blacklist peers by transport. + * + */ +static void +read_blacklist_file () +{ + char *fn; + char *data; + size_t pos; + size_t colon_pos; + int tsize; + struct GNUNET_PeerIdentity pid; + struct stat frstat; + struct GNUNET_CRYPTO_HashAsciiEncoded enc; + unsigned int entries_found; + char *transport_name; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (GST_cfg, + "TRANSPORT", + "BLACKLIST_FILE", &fn)) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Option `%s' in section `%s' not specified!\n", + "BLACKLIST_FILE", "TRANSPORT"); +#endif + return; + } + if (GNUNET_OK != GNUNET_DISK_file_test (fn)) + GNUNET_DISK_fn_write (fn, NULL, 0, + GNUNET_DISK_PERM_USER_READ | + GNUNET_DISK_PERM_USER_WRITE); + if (0 != STAT (fn, &frstat)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not read blacklist file `%s'\n"), fn); + GNUNET_free (fn); + return; + } + if (frstat.st_size == 0) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Blacklist file `%s' is empty.\n"), fn); +#endif + GNUNET_free (fn); + return; + } + /* FIXME: use mmap */ + data = GNUNET_malloc_large (frstat.st_size); + GNUNET_assert (data != NULL); + if (frstat.st_size != GNUNET_DISK_fn_read (fn, data, frstat.st_size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to read blacklist from `%s'\n"), fn); + GNUNET_free (fn); + GNUNET_free (data); + return; + } + entries_found = 0; + pos = 0; + while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos])) + pos++; + while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) && + (pos <= + frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded))) + { + colon_pos = pos; + while ((colon_pos < frstat.st_size) && + (data[colon_pos] != ':') && + (!isspace ((unsigned char) data[colon_pos]))) + colon_pos++; + if (colon_pos >= frstat.st_size) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Syntax error in blacklist file at offset %llu, giving up!\n"), + (unsigned long long) colon_pos); + GNUNET_free (fn); + GNUNET_free (data); + return; + } + + if (isspace ((unsigned char) data[colon_pos])) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Syntax error in blacklist file at offset %llu, skipping bytes.\n"), + (unsigned long long) colon_pos); + pos = colon_pos; + while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos])) + pos++; + continue; + } + tsize = colon_pos - pos; + if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) || + (tsize == 0)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Syntax error in blacklist file at offset %llu, giving up!\n"), + (unsigned long long) colon_pos); + GNUNET_free (fn); + GNUNET_free (data); + return; + } + + if (tsize < 1) + continue; + + transport_name = GNUNET_malloc (tsize + 1); + memcpy (transport_name, &data[pos], tsize); + pos = colon_pos + 1; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Read transport name `%s' in blacklist file.\n", + transport_name); +#endif + memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)); + if (!isspace + ((unsigned char) + enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1])) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Syntax error in blacklist file at offset %llu, skipping bytes.\n"), + (unsigned long long) pos); + pos++; + while ((pos < frstat.st_size) && (!isspace ((unsigned char) data[pos]))) + pos++; + GNUNET_free_non_null (transport_name); + continue; + } + enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0'; + if (GNUNET_OK != + GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"), + (unsigned long long) pos, &enc); + } + else + { + if (0 != memcmp (&pid, + &GST_my_identity, sizeof (struct GNUNET_PeerIdentity))) + { + entries_found++; + GST_blacklist_add_peer (&pid, transport_name); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Found myself `%s' in blacklist (useless, ignored)\n"), + GNUNET_i2s (&pid)); + } + } + pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded); + GNUNET_free_non_null (transport_name); + while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos])) + pos++; + } + GNUNET_STATISTICS_update (GST_stats, + "# Transport entries blacklisted", + entries_found, GNUNET_NO); + GNUNET_free (data); + GNUNET_free (fn); +} + + +/** + * Start blacklist subsystem. + * + * @param server server used to accept clients from + */ +void +GST_blacklist_start (struct GNUNET_SERVER_Handle *server) +{ + read_blacklist_file (); + GNUNET_SERVER_disconnect_notify (server, + &client_disconnect_notification, NULL); +} + + +/** + * Free the given entry in the blacklist. + * + * @param cls unused + * @param key host identity (unused) + * @param value the blacklist entry + * @return GNUNET_OK (continue to iterate) + */ +static int +free_blacklist_entry (void *cls, const GNUNET_HashCode * key, void *value) +{ + char *be = value; + + GNUNET_free (be); + return GNUNET_OK; +} + + +/** + * Stop blacklist subsystem. + */ +void +GST_blacklist_stop () +{ + if (NULL != blacklist) + { + GNUNET_CONTAINER_multihashmap_iterate (blacklist, + &free_blacklist_entry, NULL); + GNUNET_CONTAINER_multihashmap_destroy (blacklist); + blacklist = NULL; + } +} + + +/** + * Transmit blacklist query to the client. + * + * @param cls the 'struct GST_BlacklistCheck' + * @param size number of bytes allowed + * @param buf where to copy the message + * @return number of bytes copied to buf + */ +static size_t +transmit_blacklist_message (void *cls, size_t size, void *buf) +{ + struct GST_BlacklistCheck *bc = cls; + struct Blacklisters *bl; + struct BlacklistMessage bm; + + bc->th = NULL; + if (size == 0) + { + GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK); + bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to send blacklist test for peer `%s' to client\n", + GNUNET_i2s (&bc->peer)); + return 0; + } +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending blacklist test for peer `%s' to client\n", + GNUNET_i2s (&bc->peer)); +#endif + bl = bc->bl_pos; + bm.header.size = htons (sizeof (struct BlacklistMessage)); + bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY); + bm.is_allowed = htonl (0); + bm.peer = bc->peer; + memcpy (buf, &bm, sizeof (bm)); + GNUNET_SERVER_receive_done (bl->client, GNUNET_OK); + bl->waiting_for_reply = GNUNET_YES; + return sizeof (bm); +} + + +/** + * Perform next action in the blacklist check. + * + * @param cls the 'struct GST_BlacklistCheck*' + * @param tc unused + */ +static void +do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GST_BlacklistCheck *bc = cls; + struct Blacklisters *bl; + + bc->task = GNUNET_SCHEDULER_NO_TASK; + bl = bc->bl_pos; + if (bl == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No other blacklist clients active, will allow neighbour `%s'\n", + GNUNET_i2s (&bc->peer)); +#endif + bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK); + GNUNET_free (bc); + return; + } + if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO)) + return; /* someone else busy with this client */ + bl->bc = bc; + bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client, + sizeof (struct + BlacklistMessage), + GNUNET_TIME_UNIT_FOREVER_REL, + &transmit_blacklist_message, + bc); +} + + +/** + * Got the result about an existing connection from a new blacklister. + * Shutdown the neighbour if necessary. + * + * @param cls unused + * @param peer the neighbour that was investigated + * @param allowed GNUNET_OK if we can keep it, + * GNUNET_NO if we must shutdown the connection + */ +static void +confirm_or_drop_neighbour (void *cls, + const struct GNUNET_PeerIdentity *peer, int allowed) +{ + if (GNUNET_OK == allowed) + return; /* we're done */ + GNUNET_STATISTICS_update (GST_stats, + gettext_noop ("# disconnects due to blacklist"), + 1, GNUNET_NO); + GST_neighbours_force_disconnect (peer); +} + + +/** + * Closure for 'test_connection_ok'. + */ +struct TestConnectionContext +{ + /** + * Is this the first neighbour we're checking? + */ + int first; + + /** + * Handle to the blacklisting client we need to ask. + */ + struct Blacklisters *bl; +}; + + +/** + * Test if an existing connection is still acceptable given a new + * blacklisting client. + * + * @param cls the 'struct TestConnectionContest' + * @param pid neighbour's identity + * @param ats performance data + * @param ats_count number of entries in ats (excluding 0-termination) + */ +static void +test_connection_ok (void *cls, + const struct GNUNET_PeerIdentity *neighbour, + const struct GNUNET_TRANSPORT_ATS_Information *ats, + uint32_t ats_count) +{ + struct TestConnectionContext *tcc = cls; + struct GST_BlacklistCheck *bc; + + bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck)); + GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc); + bc->peer = *neighbour; + bc->cont = &confirm_or_drop_neighbour; + bc->cont_cls = NULL; + bc->bl_pos = tcc->bl; + if (GNUNET_YES == tcc->first) + { + /* all would wait for the same client, no need to + * create more than just the first task right now */ + bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc); + tcc->first = GNUNET_NO; + } +} + + + +/** + * Initialize a blacklisting client. We got a blacklist-init + * message from this client, add him to the list of clients + * to query for blacklisting. + * + * @param cls unused + * @param client the client + * @param message the blacklist-init message that was sent */ void -GNUNET_TRANSPORT_handle_blacklist_init (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +GST_blacklist_handle_init (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) { struct Blacklisters *bl; + struct TestConnectionContext tcc; + bl = bl_head; + while (bl != NULL) + { + if (bl->client == client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + bl = bl->next; + } bl = GNUNET_malloc (sizeof (struct Blacklisters)); bl->client = client; GNUNET_SERVER_client_keep (client); - GNUNET_CONTAINER_DLL_insert (bl_head, bl_tail, bl); - /* FIXME: confirm that all existing connections are OK! */ + GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl); + + /* confirm that all existing connections are OK! */ + tcc.bl = bl; + tcc.first = GNUNET_YES; + GST_neighbours_iterate (&test_connection_ok, &tcc); } /** - * Handle a request to blacklist a peer. + * A blacklisting client has sent us reply. Process it. * - * @param cls closure (always NULL) - * @param client identification of the client - * @param message the actual message + * @param cls unused + * @param client the client + * @param message the blacklist-init message that was sent */ void -GNUNET_TRANSPORT_handle_blacklist_reply (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) +GST_blacklist_handle_reply (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) { + const struct BlacklistMessage *msg = + (const struct BlacklistMessage *) message; struct Blacklisters *bl; - const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message; + struct GST_BlacklistCheck *bc; bl = bl_head; - while ( (bl != NULL) && - (bl->client != client) ) + while ((bl != NULL) && (bl->client != client)) bl = bl->next; if (bl == NULL) + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n"); +#endif + /* FIXME: other error handling here!? */ + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + bc = bl->bc; + bl->bc = NULL; + bl->waiting_for_reply = GNUNET_NO; + if (NULL != bc) + { + /* only run this if the blacklist check has not been + * cancelled in the meantime... */ + if (ntohl (msg->is_allowed) == GNUNET_SYSERR) { - GNUNET_SERVER_client_done (client, GNUNET_SYSERR); - return; +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Blacklist check failed, peer not allowed\n"); +#endif + bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO); + GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc); + GNUNET_free (bc); + } + else + { +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Blacklist check succeeded, continuing with checks\n"); +#endif + bc->bl_pos = bc->bl_pos->next; + bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc); } - if (ntohl (msg->is_allowed) == GNUNET_SYSERR) - { - be = GNUNET_malloc (sizeof (struct BlacklistEntry)); - be->peer = msg->peer; - be->client = client; - GNUNET_CONTAINER_multihashmap_put (blacklist, - &msg->peer.hashPubKey, - be, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + } + /* check if any other bc's are waiting for this blacklister */ + bc = bc_head; + for (bc = bc_head; bc != NULL; bc = bc->next) + if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task)) + { + bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc); + break; } - /* FIXME: trigger continuation... */ } /** - * Notify the given client about all entries in the blacklist. - * - * @param cls closure, refers to the 'struct GNUNET_SERVER_Client' to notify - * @param key current key code (peer identity, not used) - * @param value value in the hash map, the 'struct BlacklistEntry*' - * @return GNUNET_YES (continue to iterate) + * Add the given peer to the blacklist (for the given transport). + * + * @param peer peer to blacklist + * @param transport_name transport to blacklist for this peer, NULL for all */ -static int -notify_blacklist_entry (void *cls, - const GNUNET_HashCode *key, - void *value) -{ - struct GNUNET_SERVER_Client *client = cls; - struct BlacklistEntry *be = value; - struct BlacklistMessage msg; - - msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST); - msg.header.size = htons (sizeof (struct BlacklistMessage)); - msg.reserved = htonl (0); - msg.peer = be->peer; - msg.until = GNUNET_TIME_absolute_hton (be->until); - GNUNET_SERVER_notification_context_unicast (blacklist_notifiers, - client, - &msg.header, - GNUNET_NO); - return GNUNET_YES; +void +GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer, + const char *transport_name) +{ +#if DEBUG_TRANSPORT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding peer `%s' with plugin `%s' to blacklist\n", + GNUNET_i2s (peer), transport_name); +#endif + if (blacklist == NULL) + blacklist = + GNUNET_CONTAINER_multihashmap_create (TRANSPORT_BLACKLIST_HT_SIZE); + GNUNET_CONTAINER_multihashmap_put (blacklist, &peer->hashPubKey, + GNUNET_strdup (transport_name), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); } /** - * Handle a request for notification of blacklist changes. + * Test if the given blacklist entry matches. If so, + * abort the iteration. * - * @param cls closure (always NULL) - * @param client identification of the client - * @param message the actual message + * @param cls the transport name to match (const char*) + * @param key the key (unused) + * @param value the 'char *' (name of a blacklisted transport) + * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches */ -void -GNUNET_TRANSPORT_handle_blacklist_notify (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - GNUNET_SERVER_notification_context_add (blacklist_notifiers, client); - GNUNET_CONTAINER_multihashmap_iterate (blacklist, - ¬ify_blacklist_entry, - client); +static int +test_blacklisted (void *cls, const GNUNET_HashCode * key, void *value) +{ + const char *transport_name = cls; + char *be = value; + + if (0 == strcmp (transport_name, be)) + return GNUNET_NO; /* abort iteration! */ + return GNUNET_OK; } /** - * Is the given peer currently blacklisted? + * Test if a peer/transport combination is blacklisted. * - * @param id identity of the peer - * @return GNUNET_YES if the peer is blacklisted, GNUNET_NO if not + * @param peer the identity of the peer to test + * @param transport_name name of the transport to test, never NULL + * @param cont function to call with result + * @param cont_cls closure for 'cont' + * @return handle to the blacklist check, NULL if the decision + * was made instantly and 'cont' was already called */ -int -GNUNET_TRANSPORT_blacklist_check (const struct GNUNET_PeerIdentity *id) +struct GST_BlacklistCheck * +GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer, + const char *transport_name, + GST_BlacklistTestContinuation cont, void *cont_cls) { - if (GNUNET_CONTAINER_multihashmap_contains (blacklist, &id->hashPubKey)) - return GNUNET_YES; - + struct GST_BlacklistCheck *bc; + + if ((blacklist != NULL) && + (GNUNET_SYSERR == + GNUNET_CONTAINER_multihashmap_get_multiple (blacklist, + &peer->hashPubKey, + &test_blacklisted, + (void *) transport_name))) + { + /* disallowed by config, disapprove instantly */ + GNUNET_STATISTICS_update (GST_stats, + gettext_noop ("# disconnects due to blacklist"), + 1, GNUNET_NO); + if (cont != NULL) + cont (cont_cls, peer, GNUNET_NO); + return NULL; + } + + if (bl_head == NULL) + { + /* no blacklist clients, approve instantly */ + if (cont != NULL) + cont (cont_cls, peer, GNUNET_OK); + return NULL; + } + + /* need to query blacklist clients */ + bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck)); + GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc); + bc->peer = *peer; + bc->cont = cont; + bc->cont_cls = cont_cls; + bc->bl_pos = bl_head; + bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc); + return bc; } /** - * Initialize the blacklisting subsystem. - * - * @param server server of the transport service - * @param s scheduler to use - */ -void -GNUNET_TRANSPORT_blacklist_init (struct GNUNET_SERVER_Handle *server, - struct GNUNET_SCHEDULER_Handle *s) -{ - sched = s; - blacklist = GNUNET_CONTAINER_multihashmap_create (4); - GNUNET_SCHEDULER_add_delayed (sched, - GNUNET_TIME_UNIT_FOREVER_REL, - &shutdown_task, - NULL); + * Cancel a blacklist check. + * + * @param bc check to cancel + */ +void +GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc) +{ + GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc); + if (bc->bl_pos != NULL) + { + if (bc->bl_pos->bc == bc) + { + /* we're at the head of the queue, remove us! */ + bc->bl_pos->bc = NULL; + } + } + if (GNUNET_SCHEDULER_NO_TASK != bc->task) + { + GNUNET_SCHEDULER_cancel (bc->task); + bc->task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != bc->th) + { + GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th); + bc->th = NULL; + } + GNUNET_free (bc); } -/* end of gnunet-service-transport_blacklist.c */ +/* end of file gnunet-service-transport_blacklist.c */