LRN: Fix deps in transport
[oweals/gnunet.git] / src / transport / gnunet-service-transport_blacklist.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file transport/gnunet-service-transport_blacklist.c
23  * @brief blacklisting implementation
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-transport.h"
28 #include "gnunet-service-transport_blacklist.h"
29 #include "gnunet-service-transport_neighbours.h"
30 #include "transport.h"
31
32
33 /**
34  * Size of the blacklist hash map.
35  */
36 #define TRANSPORT_BLACKLIST_HT_SIZE 64
37
38
39 /**
40  * Context we use when performing a blacklist check.
41  */
42 struct GST_BlacklistCheck;
43
44
45 /**
46  * Information kept for each client registered to perform
47  * blacklisting.
48  */
49 struct Blacklisters
50 {
51   /**
52    * This is a linked list.
53    */
54   struct Blacklisters *next;
55
56   /**
57    * This is a linked list.
58    */
59   struct Blacklisters *prev;
60
61   /**
62    * Client responsible for this entry.
63    */
64   struct GNUNET_SERVER_Client *client;
65
66   /**
67    * Blacklist check that we're currently performing (or NULL
68    * if we're performing one that has been cancelled).
69    */
70   struct GST_BlacklistCheck *bc;
71
72   /**
73    * Set to GNUNET_YES if we're currently waiting for a reply.
74    */
75   int waiting_for_reply;
76
77 };
78
79
80
81 /**
82  * Context we use when performing a blacklist check.
83  */
84 struct GST_BlacklistCheck
85 {
86
87   /**
88    * This is a linked list.
89    */
90   struct GST_BlacklistCheck *next;
91
92   /**
93    * This is a linked list.
94    */
95   struct GST_BlacklistCheck *prev;
96
97   /**
98    * Peer being checked.
99    */
100   struct GNUNET_PeerIdentity peer;
101
102   /**
103    * Continuation to call with the result.
104    */
105   GST_BlacklistTestContinuation cont;
106
107   /**
108    * Closure for cont.
109    */
110   void *cont_cls;
111
112   /**
113    * Current transmission request handle for this client, or NULL if no
114    * request is pending.
115    */
116   struct GNUNET_CONNECTION_TransmitHandle *th;
117
118   /**
119    * Our current position in the blacklisters list.
120    */
121   struct Blacklisters *bl_pos;
122
123   /**
124    * Current task performing the check.
125    */
126   GNUNET_SCHEDULER_TaskIdentifier task;
127
128 };
129
130
131 /**
132  * Head of DLL of active blacklisting queries.
133  */
134 static struct GST_BlacklistCheck *bc_head;
135
136 /**
137  * Tail of DLL of active blacklisting queries.
138  */
139 static struct GST_BlacklistCheck *bc_tail;
140
141 /**
142  * Head of DLL of blacklisting clients.
143  */
144 static struct Blacklisters *bl_head;
145
146 /**
147  * Tail of DLL of blacklisting clients.
148  */
149 static struct Blacklisters *bl_tail;
150
151 /**
152  * Hashmap of blacklisted peers.  Values are of type 'char *' (transport names),
153  * can be NULL if we have no static blacklist.
154  */
155 static struct GNUNET_CONTAINER_MultiHashMap *blacklist;
156
157
158 /**
159  * Perform next action in the blacklist check.
160  *
161  * @param cls the 'struct BlacklistCheck*'
162  * @param tc unused
163  */
164 static void
165 do_blacklist_check (void *cls,
166                     const struct GNUNET_SCHEDULER_TaskContext *tc);
167
168
169 /**
170  * Called whenever a client is disconnected.  Frees our
171  * resources associated with that client.
172  *
173  * @param cls closure (unused)
174  * @param client identification of the client
175  */
176 static void
177 client_disconnect_notification (void *cls,
178                                 struct GNUNET_SERVER_Client *client)
179 {
180   struct Blacklisters *bl;
181   struct GST_BlacklistCheck *bc;
182
183   if (client == NULL)
184     return;
185   for (bl = bl_head; bl != NULL; bl = bl->next)
186     {
187       if (bl->client != client)
188         continue;
189       for (bc = bc_head; bc != NULL; bc = bc->next)
190         {
191           if (bc->bl_pos != bl)
192             continue;
193           bc->bl_pos = bl->next;
194           if (bc->th != NULL)
195             {
196               GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
197               bc->th = NULL;
198             }
199           if (bc->task == GNUNET_SCHEDULER_NO_TASK)
200             bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
201                                                  bc);
202           break;
203         }
204       GNUNET_CONTAINER_DLL_remove (bl_head,
205                                    bl_tail,
206                                    bl);
207       GNUNET_SERVER_client_drop (bl->client);
208       GNUNET_free (bl);
209       break;
210     }
211 }
212
213
214 /**
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.
218  *
219  */
220 static void
221 read_blacklist_file ()
222 {
223   char *fn;
224   char *data;
225   size_t pos;
226   size_t colon_pos;
227   int tsize;
228   struct GNUNET_PeerIdentity pid;
229   struct stat frstat;
230   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
231   unsigned int entries_found;
232   char *transport_name;
233
234   if (GNUNET_OK !=
235       GNUNET_CONFIGURATION_get_value_filename (GST_cfg,
236                                                "TRANSPORT",
237                                                "BLACKLIST_FILE",
238                                                &fn))
239     {
240 #if DEBUG_TRANSPORT
241       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242                   "Option `%s' in section `%s' not specified!\n",
243                   "BLACKLIST_FILE",
244                   "TRANSPORT");
245 #endif
246       return;
247     }
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))
252     {
253       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
254                   _("Could not read blacklist file `%s'\n"), 
255                   fn);
256       GNUNET_free (fn);
257       return;
258     }
259   if (frstat.st_size == 0)
260     {
261 #if DEBUG_TRANSPORT
262       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
263                   _("Blacklist file `%s' is empty.\n"),
264                   fn);
265 #endif
266       GNUNET_free (fn);
267       return;
268     }
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))
274     {
275       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
276                   _("Failed to read blacklist from `%s'\n"), 
277                   fn);
278       GNUNET_free (fn);
279       GNUNET_free (data);
280       return;
281     }
282   entries_found = 0;
283   pos = 0;
284   while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
285     pos++;
286   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
287          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
288     {
289       colon_pos = pos;
290       while ( (colon_pos < frstat.st_size) && 
291               (data[colon_pos] != ':') && 
292               (! isspace ( (unsigned char) data[colon_pos])) )
293         colon_pos++;
294       if (colon_pos >= frstat.st_size)
295         {
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);
299           GNUNET_free (fn);
300           GNUNET_free (data);
301           return;
302         }
303
304       if (isspace( (unsigned char) data[colon_pos]))
305       {
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);
309         pos = colon_pos;
310         while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
311           pos++;
312         continue;
313       }
314       tsize = colon_pos - pos;
315       if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) || (tsize == 0))
316         {
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);
320           GNUNET_free (fn);
321           GNUNET_free (data);
322           return;
323         }
324
325       if (tsize < 1)
326         continue;
327
328       transport_name = GNUNET_malloc(tsize + 1);
329       memcpy(transport_name, &data[pos], tsize);
330       pos = colon_pos + 1;
331 #if DEBUG_TRANSPORT
332       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333                   "Read transport name `%s' in blacklist file.\n",
334                   transport_name);
335 #endif
336       memcpy (&enc, 
337               &data[pos],
338               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
339       if (! isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
340         {
341           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
342                       _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
343                       (unsigned long long) pos);
344           pos++;
345           while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
346             pos++;
347           GNUNET_free_non_null(transport_name);
348           continue;
349         }
350       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
351       if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
352         {
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,
356                       &enc);
357         }
358       else
359         {
360           if (0 != memcmp (&pid,
361                            &GST_my_identity,
362                            sizeof (struct GNUNET_PeerIdentity)))
363             {
364               entries_found++;
365               GST_blacklist_add_peer (&pid,
366                                       transport_name);
367             }
368           else
369             {
370               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
371                           _("Found myself `%s' in blacklist (useless, ignored)\n"),
372                           GNUNET_i2s (&pid));
373             }
374         }
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]))
378         pos++;
379     }
380   GNUNET_STATISTICS_update (GST_stats, 
381                             "# Transport entries blacklisted",
382                             entries_found,
383                             GNUNET_NO);
384   GNUNET_free (data);
385   GNUNET_free (fn);
386 }
387
388
389 /**
390  * Start blacklist subsystem.
391  *
392  * @param server server used to accept clients from
393  */
394 void
395 GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
396 {
397   read_blacklist_file ();
398   GNUNET_SERVER_disconnect_notify (server,
399                                    &client_disconnect_notification,
400                                    NULL);
401 }
402
403
404 /**
405  * Free the given entry in the blacklist.
406  *
407  * @param cls unused
408  * @param key host identity (unused)
409  * @param value the blacklist entry
410  * @return GNUNET_OK (continue to iterate)
411  */
412 static int
413 free_blacklist_entry (void *cls,
414                       const GNUNET_HashCode *key,
415                       void *value)
416 {
417   char *be = value;
418
419   GNUNET_free (be);
420   return GNUNET_OK;
421 }
422
423
424 /**
425  * Stop blacklist subsystem.
426  */
427 void
428 GST_blacklist_stop ()
429 {
430   if (NULL != blacklist)
431     {
432       GNUNET_CONTAINER_multihashmap_iterate (blacklist,
433                                              &free_blacklist_entry,
434                                              NULL);
435       GNUNET_CONTAINER_multihashmap_destroy (blacklist);
436       blacklist = NULL;
437     }
438 }
439
440
441 /**
442  * Transmit blacklist query to the client.
443  *
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
448  */
449 static size_t
450 transmit_blacklist_message (void *cls,
451                             size_t size,
452                             void *buf)
453 {
454   struct GST_BlacklistCheck *bc = cls;
455   struct Blacklisters *bl;
456   struct BlacklistMessage bm;
457
458   bc->th = NULL;
459   if (size == 0)
460     {
461       GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
462       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
463                                            bc);
464       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
465                   "Failed to send blacklist test for peer `%s' to client\n",
466                   GNUNET_i2s (&bc->peer));
467       return 0;
468     }
469 #if DEBUG_TRANSPORT
470   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471               "Sending blacklist test for peer `%s' to client\n",
472               GNUNET_i2s (&bc->peer));
473 #endif
474   bl = bc->bl_pos;
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);
478   bm.peer = bc->peer;
479   memcpy (buf, &bm, sizeof (bm));
480   GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
481   bl->waiting_for_reply = GNUNET_YES;
482   return sizeof (bm);
483 }
484
485
486 /**
487  * Perform next action in the blacklist check.
488  *
489  * @param cls the 'struct GST_BlacklistCheck*'
490  * @param tc unused
491  */
492 static void
493 do_blacklist_check (void *cls,
494                     const struct GNUNET_SCHEDULER_TaskContext *tc)
495 {
496   struct GST_BlacklistCheck *bc = cls;
497   struct Blacklisters *bl;
498
499   bc->task = GNUNET_SCHEDULER_NO_TASK;
500   bl = bc->bl_pos;
501   if (bl == NULL)
502     {
503 #if DEBUG_TRANSPORT
504       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
505                   "No other blacklist clients active, will allow neighbour `%s'\n",
506                   GNUNET_i2s (&bc->peer));
507 #endif
508       bc->cont (bc->cont_cls,
509                 &bc->peer, 
510                 GNUNET_OK);
511       GNUNET_free (bc);
512       return;
513     }
514   if ( (bl->bc != NULL) ||
515        (bl->waiting_for_reply != GNUNET_NO) )
516     return; /* someone else busy with this client */
517   bl->bc = bc;
518   bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client,
519                                                 sizeof (struct BlacklistMessage),
520                                                 GNUNET_TIME_UNIT_FOREVER_REL,
521                                                 &transmit_blacklist_message,
522                                                 bc);    
523 }
524
525
526 /**
527  * Got the result about an existing connection from a new blacklister.
528  * Shutdown the neighbour if necessary.
529  *
530  * @param cls unused
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
534  */
535 static void
536 confirm_or_drop_neighbour (void *cls,
537                            const struct GNUNET_PeerIdentity *peer,
538                            int allowed)
539 {
540   if (GNUNET_OK == allowed)
541     return; /* we're done */
542   GNUNET_STATISTICS_update (GST_stats,
543                             gettext_noop ("# disconnects due to blacklist"),
544                             1,
545                             GNUNET_NO);
546   GST_neighbours_force_disconnect (peer);
547 }
548
549
550 /**
551  * Closure for 'test_connection_ok'.
552  */
553 struct TestConnectionContext
554 {
555   /**
556    * Is this the first neighbour we're checking?
557    */ 
558   int first;
559
560   /**
561    * Handle to the blacklisting client we need to ask.
562    */
563   struct Blacklisters *bl;
564 };
565
566
567 /**
568  * Test if an existing connection is still acceptable given a new
569  * blacklisting client.
570  *
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)
575  */
576 static void
577 test_connection_ok (void *cls,
578                     const struct GNUNET_PeerIdentity *neighbour,
579                     const struct GNUNET_TRANSPORT_ATS_Information *ats,
580                     uint32_t ats_count)
581 {
582   struct TestConnectionContext *tcc = cls;
583   struct GST_BlacklistCheck *bc;
584
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;
589   bc->cont_cls = NULL;
590   bc->bl_pos = tcc->bl;
591   if (GNUNET_YES == tcc->first)
592     { 
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,
596                                            bc);
597       tcc->first = GNUNET_NO;
598     }
599 }
600
601
602
603 /**
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.
607  *
608  * @param cls unused
609  * @param client the client
610  * @param message the blacklist-init message that was sent
611  */
612 void
613 GST_blacklist_handle_init (void *cls,
614                            struct GNUNET_SERVER_Client *client,
615                            const struct GNUNET_MessageHeader *message)
616 {
617   struct Blacklisters *bl;
618   struct TestConnectionContext tcc;
619
620   bl = bl_head;
621   while (bl != NULL)
622     {
623       if (bl->client == client)
624         {
625           GNUNET_break (0);
626           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
627           return;
628         }
629       bl = bl->next;
630     }
631   bl = GNUNET_malloc (sizeof (struct Blacklisters));
632   bl->client = client;
633   GNUNET_SERVER_client_keep (client);
634   GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
635
636   /* confirm that all existing connections are OK! */
637   tcc.bl = bl;
638   tcc.first = GNUNET_YES;
639   GST_neighbours_iterate (&test_connection_ok,
640                           &tcc);
641 }
642
643
644 /**
645  * A blacklisting client has sent us reply. Process it.
646  *
647  * @param cls unused
648  * @param client the client
649  * @param message the blacklist-init message that was sent
650  */
651 void
652 GST_blacklist_handle_reply (void *cls,
653                             struct GNUNET_SERVER_Client *client,
654                             const struct GNUNET_MessageHeader *message)
655 {
656   const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
657   struct Blacklisters *bl;
658   struct GST_BlacklistCheck *bc;
659
660   bl = bl_head;
661   while ( (bl != NULL) &&
662           (bl->client != client) )
663     bl = bl->next;
664   if (bl == NULL)
665     {
666 #if DEBUG_TRANSPORT
667       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668                   "Blacklist client disconnected\n");
669 #endif
670       /* FIXME: other error handling here!? */
671       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
672       return;
673     }
674   bc = bl->bc;
675   bl->bc = NULL;
676   bl->waiting_for_reply = GNUNET_NO;
677   if (NULL != bc)
678     {
679       /* only run this if the blacklist check has not been 
680          cancelled in the meantime... */
681       if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
682         {
683 #if DEBUG_TRANSPORT
684           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685                       "Blacklist check failed, peer not allowed\n");
686 #endif
687           bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
688           GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
689           GNUNET_free (bc);
690         }
691       else
692         {
693 #if DEBUG_TRANSPORT
694           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
695                       "Blacklist check succeeded, continuing with checks\n");
696 #endif
697           bc->bl_pos = bc->bl_pos->next;
698           bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
699                                                bc);
700         }
701     }
702   /* check if any other bc's are waiting for this blacklister */
703   bc = bc_head;
704   for (bc = bc_head; bc != NULL; bc = bc->next)
705     if ( (bc->bl_pos == bl) &&
706          (GNUNET_SCHEDULER_NO_TASK == bc->task) )
707       {
708         bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
709                                              bc);
710         break;
711       }
712 }
713
714
715 /**
716  * Add the given peer to the blacklist (for the given transport).
717  * 
718  * @param peer peer to blacklist
719  * @param transport_name transport to blacklist for this peer, NULL for all
720  */
721 void
722 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
723                         const char *transport_name)
724 {
725 #if DEBUG_TRANSPORT
726   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727               "Adding peer `%s' with plugin `%s' to blacklist\n",
728               GNUNET_i2s (peer),
729               transport_name);
730 #endif
731   if (blacklist == NULL)
732     blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);
733   GNUNET_CONTAINER_multihashmap_put (blacklist,
734                                      &peer->hashPubKey,
735                                      GNUNET_strdup (transport_name),
736                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
737 }
738
739
740 /**
741  * Test if the given blacklist entry matches.  If so,
742  * abort the iteration.
743  *
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
748  */
749 static int
750 test_blacklisted (void *cls,
751                   const GNUNET_HashCode *key,
752                   void *value)
753 {
754   const char *transport_name = cls;
755   char *be = value;
756
757   if (0 == strcmp (transport_name,
758                    be))
759     return GNUNET_NO; /* abort iteration! */
760   return GNUNET_OK;
761 }
762
763
764 /**
765  * Test if a peer/transport combination is blacklisted.
766  *
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
772  */
773 struct GST_BlacklistCheck *
774 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
775                             const char *transport_name,
776                             GST_BlacklistTestContinuation cont,
777                             void *cont_cls)
778 {
779   struct GST_BlacklistCheck *bc;
780   
781   if ( (blacklist != NULL) &&
782        (GNUNET_SYSERR ==
783         GNUNET_CONTAINER_multihashmap_get_multiple (blacklist,
784                                                     &peer->hashPubKey,
785                                                     &test_blacklisted,
786                                                     (void*) transport_name)) )
787     {
788       /* disallowed by config, disapprove instantly */
789       GNUNET_STATISTICS_update (GST_stats,
790                                 gettext_noop ("# disconnects due to blacklist"),
791                                 1,
792                                 GNUNET_NO);
793       if (cont != NULL)
794         cont (cont_cls, peer, GNUNET_NO);
795       return;
796     }
797
798   if (bl_head == NULL)
799     {
800       /* no blacklist clients, approve instantly */
801       if (cont != NULL)
802         cont (cont_cls, peer, GNUNET_OK);
803       return;
804     }
805
806   /* need to query blacklist clients */
807   bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
808   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
809   bc->peer = *peer;
810   bc->cont = cont;
811   bc->cont_cls = cont_cls;
812   bc->bl_pos = bl_head;
813   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
814                                        bc);
815   return bc;
816 }
817                                          
818
819 /**
820  * Cancel a blacklist check.
821  * 
822  * @param bc check to cancel
823  */
824 void
825 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
826 {
827   GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
828   if (bc->bl_pos != NULL)
829     {
830       if (bc->bl_pos->bc == bc)
831         {
832           /* we're at the head of the queue, remove us! */
833           bc->bl_pos->bc = NULL;
834         }
835     }
836   if (GNUNET_SCHEDULER_NO_TASK != bc->task)
837     {
838       GNUNET_SCHEDULER_cancel (bc->task);
839       bc->task = GNUNET_SCHEDULER_NO_TASK;
840     }
841   if (NULL != bc->th)
842     {
843       GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
844       bc->th = NULL;
845     }
846   GNUNET_free (bc);
847 }                                        
848
849
850 /* end of file gnunet-service-transport_blacklist.c */