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