- minor
[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, const struct GNUNET_SCHEDULER_TaskContext *tc);
166
167
168 /**
169  * Called whenever a client is disconnected.  Frees our
170  * resources associated with that client.
171  *
172  * @param cls closure (unused)
173  * @param client identification of the client
174  */
175 static void
176 client_disconnect_notification (void *cls, struct GNUNET_SERVER_Client *client)
177 {
178   struct Blacklisters *bl;
179   struct GST_BlacklistCheck *bc;
180
181   if (client == NULL)
182     return;
183   for (bl = bl_head; bl != NULL; bl = bl->next)
184   {
185     if (bl->client != client)
186       continue;
187     for (bc = bc_head; bc != NULL; bc = bc->next)
188     {
189       if (bc->bl_pos != bl)
190         continue;
191       bc->bl_pos = bl->next;
192       if (bc->th != NULL)
193       {
194         GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
195         bc->th = NULL;
196       }
197       if (bc->task == GNUNET_SCHEDULER_NO_TASK)
198         bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
199       break;
200     }
201     GNUNET_CONTAINER_DLL_remove (bl_head, bl_tail, bl);
202     GNUNET_SERVER_client_drop (bl->client);
203     GNUNET_free (bl);
204     break;
205   }
206 }
207
208
209 /**
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.
213  *
214  */
215 static void
216 read_blacklist_file ()
217 {
218   char *fn;
219   char *data;
220   size_t pos;
221   size_t colon_pos;
222   int tsize;
223   struct GNUNET_PeerIdentity pid;
224   struct stat frstat;
225   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
226   unsigned int entries_found;
227   char *transport_name;
228
229   if (GNUNET_OK !=
230       GNUNET_CONFIGURATION_get_value_filename (GST_cfg, "TRANSPORT",
231                                                "BLACKLIST_FILE", &fn))
232   {
233 #if DEBUG_TRANSPORT
234     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
235                 "Option `%s' in section `%s' not specified!\n",
236                 "BLACKLIST_FILE", "TRANSPORT");
237 #endif
238     return;
239   }
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 (0 != STAT (fn, &frstat))
245   {
246     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
247                 _("Could not read blacklist file `%s'\n"), fn);
248     GNUNET_free (fn);
249     return;
250   }
251   if (frstat.st_size == 0)
252   {
253 #if DEBUG_TRANSPORT
254     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Blacklist file `%s' is empty.\n"),
255                 fn);
256 #endif
257     GNUNET_free (fn);
258     return;
259   }
260   /* FIXME: use mmap */
261   data = GNUNET_malloc_large (frstat.st_size);
262   GNUNET_assert (data != NULL);
263   if (frstat.st_size != GNUNET_DISK_fn_read (fn, data, frstat.st_size))
264   {
265     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
266                 _("Failed to read blacklist from `%s'\n"), fn);
267     GNUNET_free (fn);
268     GNUNET_free (data);
269     return;
270   }
271   entries_found = 0;
272   pos = 0;
273   while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos]))
274     pos++;
275   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
276          (pos <=
277           frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
278   {
279     colon_pos = pos;
280     while ((colon_pos < frstat.st_size) && (data[colon_pos] != ':') &&
281            (!isspace ((unsigned char) data[colon_pos])))
282       colon_pos++;
283     if (colon_pos >= frstat.st_size)
284     {
285       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
286                   _
287                   ("Syntax error in blacklist file at offset %llu, giving up!\n"),
288                   (unsigned long long) colon_pos);
289       GNUNET_free (fn);
290       GNUNET_free (data);
291       return;
292     }
293
294     if (isspace ((unsigned char) data[colon_pos]))
295     {
296       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
297                   _
298                   ("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
299                   (unsigned long long) colon_pos);
300       pos = colon_pos;
301       while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos]))
302         pos++;
303       continue;
304     }
305     tsize = colon_pos - pos;
306     if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) ||
307         (tsize == 0))
308     {
309       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
310                   _
311                   ("Syntax error in blacklist file at offset %llu, giving up!\n"),
312                   (unsigned long long) colon_pos);
313       GNUNET_free (fn);
314       GNUNET_free (data);
315       return;
316     }
317
318     if (tsize < 1)
319       continue;
320
321     transport_name = GNUNET_malloc (tsize + 1);
322     memcpy (transport_name, &data[pos], tsize);
323     pos = colon_pos + 1;
324 #if DEBUG_TRANSPORT
325     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326                 "Read transport name `%s' in blacklist file.\n",
327                 transport_name);
328 #endif
329     memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
330     if (!isspace
331         ((unsigned char)
332          enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
333     {
334       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
335                   _
336                   ("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
337                   (unsigned long long) pos);
338       pos++;
339       while ((pos < frstat.st_size) && (!isspace ((unsigned char) data[pos])))
340         pos++;
341       GNUNET_free_non_null (transport_name);
342       continue;
343     }
344     enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
345     if (GNUNET_OK !=
346         GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
347     {
348       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
349                   _
350                   ("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
351                   (unsigned long long) pos, &enc);
352     }
353     else
354     {
355       if (0 !=
356           memcmp (&pid, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
357       {
358         entries_found++;
359         GST_blacklist_add_peer (&pid, transport_name);
360       }
361       else
362       {
363         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
364                     _("Found myself `%s' in blacklist (useless, ignored)\n"),
365                     GNUNET_i2s (&pid));
366       }
367     }
368     pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
369     GNUNET_free_non_null (transport_name);
370     while ((pos < frstat.st_size) && isspace ((unsigned char) data[pos]))
371       pos++;
372   }
373   GNUNET_STATISTICS_update (GST_stats, "# Transport entries blacklisted",
374                             entries_found, GNUNET_NO);
375   GNUNET_free (data);
376   GNUNET_free (fn);
377 }
378
379
380 /**
381  * Start blacklist subsystem.
382  *
383  * @param server server used to accept clients from
384  */
385 void
386 GST_blacklist_start (struct GNUNET_SERVER_Handle *server)
387 {
388   read_blacklist_file ();
389   GNUNET_SERVER_disconnect_notify (server, &client_disconnect_notification,
390                                    NULL);
391 }
392
393
394 /**
395  * Free the given entry in the blacklist.
396  *
397  * @param cls unused
398  * @param key host identity (unused)
399  * @param value the blacklist entry
400  * @return GNUNET_OK (continue to iterate)
401  */
402 static int
403 free_blacklist_entry (void *cls, const GNUNET_HashCode * key, void *value)
404 {
405   char *be = value;
406
407   GNUNET_free (be);
408   return GNUNET_OK;
409 }
410
411
412 /**
413  * Stop blacklist subsystem.
414  */
415 void
416 GST_blacklist_stop ()
417 {
418   if (NULL != blacklist)
419   {
420     GNUNET_CONTAINER_multihashmap_iterate (blacklist, &free_blacklist_entry,
421                                            NULL);
422     GNUNET_CONTAINER_multihashmap_destroy (blacklist);
423     blacklist = NULL;
424   }
425 }
426
427
428 /**
429  * Transmit blacklist query to the client.
430  *
431  * @param cls the 'struct GST_BlacklistCheck'
432  * @param size number of bytes allowed
433  * @param buf where to copy the message
434  * @return number of bytes copied to buf
435  */
436 static size_t
437 transmit_blacklist_message (void *cls, size_t size, void *buf)
438 {
439   struct GST_BlacklistCheck *bc = cls;
440   struct Blacklisters *bl;
441   struct BlacklistMessage bm;
442
443   bc->th = NULL;
444   if (size == 0)
445   {
446     GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
447     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
448     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
449                 "Failed to send blacklist test for peer `%s' to client\n",
450                 GNUNET_i2s (&bc->peer));
451     return 0;
452   }
453 #if DEBUG_TRANSPORT
454   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
455               "Sending blacklist test for peer `%s' to client\n",
456               GNUNET_i2s (&bc->peer));
457 #endif
458   bl = bc->bl_pos;
459   bm.header.size = htons (sizeof (struct BlacklistMessage));
460   bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
461   bm.is_allowed = htonl (0);
462   bm.peer = bc->peer;
463   memcpy (buf, &bm, sizeof (bm));
464   GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
465   bl->waiting_for_reply = GNUNET_YES;
466   return sizeof (bm);
467 }
468
469
470 /**
471  * Perform next action in the blacklist check.
472  *
473  * @param cls the 'struct GST_BlacklistCheck*'
474  * @param tc unused
475  */
476 static void
477 do_blacklist_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
478 {
479   struct GST_BlacklistCheck *bc = cls;
480   struct Blacklisters *bl;
481
482   bc->task = GNUNET_SCHEDULER_NO_TASK;
483   bl = bc->bl_pos;
484   if (bl == NULL)
485   {
486 #if DEBUG_TRANSPORT
487     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488                 "No other blacklist clients active, will allow neighbour `%s'\n",
489                 GNUNET_i2s (&bc->peer));
490 #endif
491     bc->cont (bc->cont_cls, &bc->peer, GNUNET_OK);
492     GNUNET_CONTAINER_DLL_remove(bc_head, bc_tail, bc);
493     GNUNET_free (bc);
494     return;
495   }
496   if ((bl->bc != NULL) || (bl->waiting_for_reply != GNUNET_NO))
497     return;                     /* someone else busy with this client */
498   bl->bc = bc;
499   bc->th =
500       GNUNET_SERVER_notify_transmit_ready (bl->client,
501                                            sizeof (struct BlacklistMessage),
502                                            GNUNET_TIME_UNIT_FOREVER_REL,
503                                            &transmit_blacklist_message, bc);
504 }
505
506
507 /**
508  * Got the result about an existing connection from a new blacklister.
509  * Shutdown the neighbour if necessary.
510  *
511  * @param cls unused
512  * @param peer the neighbour that was investigated
513  * @param allowed GNUNET_OK if we can keep it,
514  *                GNUNET_NO if we must shutdown the connection
515  */
516 static void
517 confirm_or_drop_neighbour (void *cls, const struct GNUNET_PeerIdentity *peer,
518                            int allowed)
519 {
520   if (GNUNET_OK == allowed)
521     return;                     /* we're done */
522   GNUNET_STATISTICS_update (GST_stats,
523                             gettext_noop ("# disconnects due to blacklist"), 1,
524                             GNUNET_NO);
525   GST_neighbours_force_disconnect (peer);
526 }
527
528
529 /**
530  * Closure for 'test_connection_ok'.
531  */
532 struct TestConnectionContext
533 {
534   /**
535    * Is this the first neighbour we're checking?
536    */
537   int first;
538
539   /**
540    * Handle to the blacklisting client we need to ask.
541    */
542   struct Blacklisters *bl;
543 };
544
545
546 /**
547  * Test if an existing connection is still acceptable given a new
548  * blacklisting client.
549  *
550  * @param cls the 'struct TestConnectionContest'
551  * @param neighbour neighbour's identity
552  * @param ats performance data
553  * @param ats_count number of entries in ats (excluding 0-termination)
554  * @param address the address
555  */
556 static void
557 test_connection_ok (void *cls, const struct GNUNET_PeerIdentity *neighbour,
558                     const struct GNUNET_ATS_Information *ats,
559                     uint32_t ats_count,
560                     const struct GNUNET_HELLO_Address *address)
561 {
562   struct TestConnectionContext *tcc = cls;
563   struct GST_BlacklistCheck *bc;
564
565   bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
566   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
567   bc->peer = *neighbour;
568   bc->cont = &confirm_or_drop_neighbour;
569   bc->cont_cls = NULL;
570   bc->bl_pos = tcc->bl;
571   if (GNUNET_YES == tcc->first)
572   {
573     /* all would wait for the same client, no need to
574      * create more than just the first task right now */
575     bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
576     tcc->first = GNUNET_NO;
577   }
578 }
579
580
581
582 /**
583  * Initialize a blacklisting client.  We got a blacklist-init
584  * message from this client, add him to the list of clients
585  * to query for blacklisting.
586  *
587  * @param cls unused
588  * @param client the client
589  * @param message the blacklist-init message that was sent
590  */
591 void
592 GST_blacklist_handle_init (void *cls, struct GNUNET_SERVER_Client *client,
593                            const struct GNUNET_MessageHeader *message)
594 {
595   struct Blacklisters *bl;
596   struct TestConnectionContext tcc;
597
598   bl = bl_head;
599   while (bl != NULL)
600   {
601     if (bl->client == client)
602     {
603       GNUNET_break (0);
604       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
605       return;
606     }
607     bl = bl->next;
608   }
609   bl = GNUNET_malloc (sizeof (struct Blacklisters));
610   bl->client = client;
611   GNUNET_SERVER_client_keep (client);
612   GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
613
614   /* confirm that all existing connections are OK! */
615   tcc.bl = bl;
616   tcc.first = GNUNET_YES;
617   GST_neighbours_iterate (&test_connection_ok, &tcc);
618 }
619
620
621 /**
622  * A blacklisting client has sent us reply. Process it.
623  *
624  * @param cls unused
625  * @param client the client
626  * @param message the blacklist-init message that was sent
627  */
628 void
629 GST_blacklist_handle_reply (void *cls, struct GNUNET_SERVER_Client *client,
630                             const struct GNUNET_MessageHeader *message)
631 {
632   const struct BlacklistMessage *msg =
633       (const struct BlacklistMessage *) message;
634   struct Blacklisters *bl;
635   struct GST_BlacklistCheck *bc;
636
637   bl = bl_head;
638   while ((bl != NULL) && (bl->client != client))
639     bl = bl->next;
640   if (bl == NULL)
641   {
642 #if DEBUG_TRANSPORT
643     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Blacklist client disconnected\n");
644 #endif
645     /* FIXME: other error handling here!? */
646     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
647     return;
648   }
649   bc = bl->bc;
650   bl->bc = NULL;
651   bl->waiting_for_reply = GNUNET_NO;
652   if (NULL != bc)
653   {
654     /* only run this if the blacklist check has not been
655      * cancelled in the meantime... */
656     if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
657     {
658 #if DEBUG_TRANSPORT
659       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660                   "Blacklist check failed, peer not allowed\n");
661 #endif
662       bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
663       GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
664       GNUNET_free (bc);
665     }
666     else
667     {
668 #if DEBUG_TRANSPORT
669       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
670                   "Blacklist check succeeded, continuing with checks\n");
671 #endif
672       bc->bl_pos = bc->bl_pos->next;
673       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
674     }
675   }
676   /* check if any other bc's are waiting for this blacklister */
677   bc = bc_head;
678   for (bc = bc_head; bc != NULL; bc = bc->next)
679     if ((bc->bl_pos == bl) && (GNUNET_SCHEDULER_NO_TASK == bc->task))
680     {
681       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
682       break;
683     }
684 }
685
686
687 /**
688  * Add the given peer to the blacklist (for the given transport).
689  *
690  * @param peer peer to blacklist
691  * @param transport_name transport to blacklist for this peer, NULL for all
692  */
693 void
694 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
695                         const char *transport_name)
696 {
697 #if DEBUG_TRANSPORT
698   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
699               "Adding peer `%s' with plugin `%s' to blacklist\n",
700               GNUNET_i2s (peer), transport_name);
701 #endif
702   if (blacklist == NULL)
703     blacklist =
704         GNUNET_CONTAINER_multihashmap_create (TRANSPORT_BLACKLIST_HT_SIZE);
705   GNUNET_CONTAINER_multihashmap_put (blacklist, &peer->hashPubKey,
706                                      GNUNET_strdup (transport_name),
707                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
708 }
709
710
711 /**
712  * Test if the given blacklist entry matches.  If so,
713  * abort the iteration.
714  *
715  * @param cls the transport name to match (const char*)
716  * @param key the key (unused)
717  * @param value the 'char *' (name of a blacklisted transport)
718  * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
719  */
720 static int
721 test_blacklisted (void *cls, const GNUNET_HashCode * key, void *value)
722 {
723   const char *transport_name = cls;
724   char *be = value;
725
726   /* blacklist check for specific no specific transport*/
727   if (transport_name == NULL)
728     return GNUNET_NO;
729
730   /* blacklist check for specific transport */
731   if (0 == strcmp (transport_name, be))
732     return GNUNET_NO;           /* abort iteration! */
733   return GNUNET_OK;
734 }
735
736
737 /**
738  * Test if a peer/transport combination is blacklisted.
739  *
740  * @param peer the identity of the peer to test
741  * @param transport_name name of the transport to test, never NULL
742  * @param cont function to call with result
743  * @param cont_cls closure for 'cont'
744  * @return handle to the blacklist check, NULL if the decision
745  *        was made instantly and 'cont' was already called
746  */
747 struct GST_BlacklistCheck *
748 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
749                             const char *transport_name,
750                             GST_BlacklistTestContinuation cont, void *cont_cls)
751 {
752   struct GST_BlacklistCheck *bc;
753
754   GNUNET_assert (peer != NULL);
755
756   if ((blacklist != NULL) &&
757       (GNUNET_SYSERR ==
758        GNUNET_CONTAINER_multihashmap_get_multiple (blacklist, &peer->hashPubKey,
759                                                    &test_blacklisted,
760                                                    (void *) transport_name)))
761   {
762     /* disallowed by config, disapprove instantly */
763     GNUNET_STATISTICS_update (GST_stats,
764                               gettext_noop ("# disconnects due to blacklist"),
765                               1, GNUNET_NO);
766     if (cont != NULL)
767       cont (cont_cls, peer, GNUNET_NO);
768     return NULL;
769   }
770
771   if (bl_head == NULL)
772   {
773     /* no blacklist clients, approve instantly */
774     if (cont != NULL)
775       cont (cont_cls, peer, GNUNET_OK);
776     return NULL;
777   }
778
779   /* need to query blacklist clients */
780   bc = GNUNET_malloc (sizeof (struct GST_BlacklistCheck));
781   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
782   bc->peer = *peer;
783   bc->cont = cont;
784   bc->cont_cls = cont_cls;
785   bc->bl_pos = bl_head;
786   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check, bc);
787   return bc;
788 }
789
790
791 /**
792  * Cancel a blacklist check.
793  *
794  * @param bc check to cancel
795  */
796 void
797 GST_blacklist_test_cancel (struct GST_BlacklistCheck *bc)
798 {
799   GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
800   if (bc->bl_pos != NULL)
801   {
802     if (bc->bl_pos->bc == bc)
803     {
804       /* we're at the head of the queue, remove us! */
805       bc->bl_pos->bc = NULL;
806     }
807   }
808   if (GNUNET_SCHEDULER_NO_TASK != bc->task)
809   {
810     GNUNET_SCHEDULER_cancel (bc->task);
811     bc->task = GNUNET_SCHEDULER_NO_TASK;
812   }
813   if (NULL != bc->th)
814   {
815     GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
816     bc->th = NULL;
817   }
818   GNUNET_free (bc);
819 }
820
821
822 /* end of file gnunet-service-transport_blacklist.c */