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