aa4644d105b033e26ae93f408b942896c1a88efd
[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 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.
68    */
69   struct BlacklistCheck *bc;
70
71 };
72
73
74
75 /**
76  * Context we use when performing a blacklist check.
77  */
78 struct BlacklistCheck
79 {
80
81   /**
82    * This is a linked list.
83    */
84   struct BlacklistCheck *next;
85
86   /**
87    * This is a linked list.
88    */
89   struct BlacklistCheck *prev;
90
91   /**
92    * Peer being checked.
93    */
94   struct GNUNET_PeerIdentity peer;
95
96   /**
97    * Continuation to call with the result.
98    */
99   GST_BlacklistTestContinuation cont;
100
101   /**
102    * Closure for cont.
103    */
104   void *cont_cls;
105
106   /**
107    * Current transmission request handle for this client, or NULL if no
108    * request is pending.
109    */
110   struct GNUNET_CONNECTION_TransmitHandle *th;
111
112   /**
113    * Our current position in the blacklisters list.
114    */
115   struct Blacklisters *bl_pos;
116
117   /**
118    * Current task performing the check.
119    */
120   GNUNET_SCHEDULER_TaskIdentifier task;
121
122 };
123
124
125 /**
126  * Head of DLL of active blacklisting queries.
127  */
128 static struct BlacklistCheck *bc_head;
129
130 /**
131  * Tail of DLL of active blacklisting queries.
132  */
133 static struct BlacklistCheck *bc_tail;
134
135 /**
136  * Head of DLL of blacklisting clients.
137  */
138 static struct Blacklisters *bl_head;
139
140 /**
141  * Tail of DLL of blacklisting clients.
142  */
143 static struct Blacklisters *bl_tail;
144
145 /**
146  * Hashmap of blacklisted peers.  Values are of type 'char *' (transport names),
147  * can be NULL if we have no static blacklist.
148  */
149 static struct GNUNET_CONTAINER_MultiHashMap *blacklist;
150
151
152 /**
153  * Perform next action in the blacklist check.
154  *
155  * @param cls the 'struct BlacklistCheck*'
156  * @param tc unused
157  */
158 static void
159 do_blacklist_check (void *cls,
160                     const struct GNUNET_SCHEDULER_TaskContext *tc);
161
162
163 /**
164  * Called whenever a client is disconnected.  Frees our
165  * resources associated with that client.
166  *
167  * @param cls closure (unused)
168  * @param client identification of the client
169  */
170 static void
171 client_disconnect_notification (void *cls,
172                                 struct GNUNET_SERVER_Client *client)
173 {
174   struct Blacklisters *bl;
175   struct BlacklistCheck *bc;
176
177   if (client == NULL)
178     return;
179   for (bl = bl_head; bl != NULL; bl = bl->next)
180     {
181       if (bl->client != client)
182         continue;
183       for (bc = bc_head; bc != NULL; bc = bc->next)
184         {
185           if (bc->bl_pos != bl)
186             continue;
187           bc->bl_pos = bl->next;
188           if (bc->th != NULL)
189             {
190               GNUNET_CONNECTION_notify_transmit_ready_cancel (bc->th);
191               bc->th = NULL;
192             }
193           if (bc->task == GNUNET_SCHEDULER_NO_TASK)
194             bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
195                                                  bc);
196           break;
197         }
198       GNUNET_CONTAINER_DLL_remove (bl_head,
199                                    bl_tail,
200                                    bl);
201       GNUNET_SERVER_client_drop (bl->client);
202       GNUNET_free (bl);
203       break;
204     }
205 }
206
207
208 /**
209  * Read the blacklist file, containing transport:peer entries.
210  * Provided the transport is loaded, set up hashmap with these
211  * entries to blacklist peers by transport.
212  *
213  */
214 static void
215 read_blacklist_file ()
216 {
217   char *fn;
218   char *data;
219   size_t pos;
220   size_t colon_pos;
221   int tsize;
222   struct GNUNET_PeerIdentity pid;
223   struct stat frstat;
224   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
225   unsigned int entries_found;
226   char *transport_name;
227
228   if (GNUNET_OK !=
229       GNUNET_CONFIGURATION_get_value_filename (GST_cfg,
230                                                "TRANSPORT",
231                                                "BLACKLIST_FILE",
232                                                &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",
238                   "TRANSPORT");
239 #endif
240       return;
241     }
242   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
243     GNUNET_DISK_fn_write (fn, NULL, 0, 
244                           GNUNET_DISK_PERM_USER_READ | 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"), 
249                   fn);
250       GNUNET_free (fn);
251       return;
252     }
253   if (frstat.st_size == 0)
254     {
255 #if DEBUG_TRANSPORT
256       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
257                   _("Blacklist file `%s' is empty.\n"),
258                   fn);
259 #endif
260       GNUNET_free (fn);
261       return;
262     }
263   /* FIXME: use mmap */
264   data = GNUNET_malloc_large (frstat.st_size);
265   GNUNET_assert(data != NULL);
266   if (frstat.st_size !=
267       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
268     {
269       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
270                   _("Failed to read blacklist from `%s'\n"), 
271                   fn);
272       GNUNET_free (fn);
273       GNUNET_free (data);
274       return;
275     }
276   entries_found = 0;
277   pos = 0;
278   while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
279     pos++;
280   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
281          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
282     {
283       colon_pos = pos;
284       while ( (colon_pos < frstat.st_size) && 
285               (data[colon_pos] != ':') && 
286               (! isspace ( (unsigned char) data[colon_pos])) )
287         colon_pos++;
288       if (colon_pos >= frstat.st_size)
289         {
290           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
291                       _("Syntax error in blacklist file at offset %llu, giving up!\n"),
292                       (unsigned long long) colon_pos);
293           GNUNET_free (fn);
294           GNUNET_free (data);
295           return;
296         }
297
298       if (isspace( (unsigned char) data[colon_pos]))
299       {
300         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
301                     _("Syntax error in blacklist file at offset %llu, skipping bytes.\n"),
302                     (unsigned long long) colon_pos);
303         pos = colon_pos;
304         while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
305           pos++;
306         continue;
307       }
308       tsize = colon_pos - pos;
309       if ((pos >= frstat.st_size) || (pos + tsize >= frstat.st_size) || (tsize == 0))
310         {
311           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
312                       _("Syntax error in blacklist file at offset %llu, giving up!\n"),
313                       (unsigned long long) colon_pos);
314           GNUNET_free (fn);
315           GNUNET_free (data);
316           return;
317         }
318
319       if (tsize < 1)
320         continue;
321
322       transport_name = GNUNET_malloc(tsize + 1);
323       memcpy(transport_name, &data[pos], tsize);
324       pos = colon_pos + 1;
325 #if DEBUG_TRANSPORT
326       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327                   "Read transport name `%s' in blacklist file.\n",
328                   transport_name);
329 #endif
330       memcpy (&enc, 
331               &data[pos],
332               sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
333       if (! isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
334         {
335           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
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 != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
346         {
347           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
348                       _("Syntax error in blacklist file at offset %llu, skipping bytes `%s'.\n"),
349                       (unsigned long long) pos,
350                       &enc);
351         }
352       else
353         {
354           if (0 != memcmp (&pid,
355                            &GST_my_identity,
356                            sizeof (struct GNUNET_PeerIdentity)))
357             {
358               entries_found++;
359               GST_blacklist_add_peer (&pid,
360                                       transport_name);
361             }
362           else
363             {
364               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
365                           _("Found myself `%s' in blacklist (useless, ignored)\n"),
366                           GNUNET_i2s (&pid));
367             }
368         }
369       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
370       GNUNET_free_non_null(transport_name);
371       while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
372         pos++;
373     }
374   GNUNET_STATISTICS_update (GST_stats, 
375                             "# Transport entries blacklisted",
376                             entries_found,
377                             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,
394                                    NULL);
395 }
396
397
398 /**
399  * Free the given entry in the blacklist.
400  *
401  * @param cls unused
402  * @param key host identity (unused)
403  * @param value the blacklist entry
404  * @return GNUNET_OK (continue to iterate)
405  */
406 static int
407 free_blacklist_entry (void *cls,
408                       const GNUNET_HashCode *key,
409                       void *value)
410 {
411   char *be = value;
412
413   GNUNET_free (be);
414   return GNUNET_OK;
415 }
416
417
418 /**
419  * Stop blacklist subsystem.
420  */
421 void
422 GST_blacklist_stop ()
423 {
424   if (NULL != blacklist)
425     {
426       GNUNET_CONTAINER_multihashmap_iterate (blacklist,
427                                              &free_blacklist_entry,
428                                              NULL);
429       GNUNET_CONTAINER_multihashmap_destroy (blacklist);
430       blacklist = NULL;
431     }
432 }
433
434
435 /**
436  * Transmit blacklist query to the client.
437  *
438  * @param cls the 'struct BlacklistCheck'
439  * @param size number of bytes allowed
440  * @param buf where to copy the message
441  * @return number of bytes copied to buf
442  */
443 static size_t
444 transmit_blacklist_message (void *cls,
445                             size_t size,
446                             void *buf)
447 {
448   struct BlacklistCheck *bc = cls;
449   struct Blacklisters *bl;
450   struct BlacklistMessage bm;
451
452   bc->th = NULL;
453   if (size == 0)
454     {
455       GNUNET_assert (bc->task == GNUNET_SCHEDULER_NO_TASK);
456       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
457                                            bc);
458       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
459                   "Failed to send blacklist test for peer `%s' to client\n",
460                   GNUNET_i2s (&bc->peer));
461       return 0;
462     }
463 #if DEBUG_TRANSPORT
464   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465               "Sending blacklist test for peer `%s' to client\n",
466               GNUNET_i2s (&bc->peer));
467 #endif
468   bl = bc->bl_pos;
469   bm.header.size = htons (sizeof (struct BlacklistMessage));
470   bm.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BLACKLIST_QUERY);
471   bm.is_allowed = htonl (0);
472   bm.peer = bc->peer;
473   memcpy (buf, &bm, sizeof (bm));
474   GNUNET_SERVER_receive_done (bl->client, GNUNET_OK);
475   return sizeof (bm);
476 }
477
478
479 /**
480  * Perform next action in the blacklist check.
481  *
482  * @param cls the 'struct BlacklistCheck*'
483  * @param tc unused
484  */
485 static void
486 do_blacklist_check (void *cls,
487                     const struct GNUNET_SCHEDULER_TaskContext *tc)
488 {
489   struct BlacklistCheck *bc = cls;
490   struct Blacklisters *bl;
491
492   bc->task = GNUNET_SCHEDULER_NO_TASK;
493   bl = bc->bl_pos;
494   if (bl == NULL)
495     {
496 #if DEBUG_TRANSPORT
497       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498                   "No other blacklist clients active, will allow neighbour `%s'\n",
499                   GNUNET_i2s (&bc->peer));
500 #endif
501       bc->cont (bc->cont_cls,
502                 &bc->peer, 
503                 GNUNET_OK);
504       GNUNET_free (bc);
505       return;
506     }
507   if (bl->bc != NULL)
508     return; /* someone else busy with this client */
509   bl->bc = bc;
510   bc->th = GNUNET_SERVER_notify_transmit_ready (bl->client,
511                                                 sizeof (struct BlacklistMessage),
512                                                 GNUNET_TIME_UNIT_FOREVER_REL,
513                                                 &transmit_blacklist_message,
514                                                 bc);    
515 }
516
517
518 /**
519  * Got the result about an existing connection from a new blacklister.
520  * Shutdown the neighbour if necessary.
521  *
522  * @param cls unused
523  * @param peer the neighbour that was investigated
524  * @param allowed GNUNET_OK if we can keep it,
525  *                GNUNET_NO if we must shutdown the connection
526  */
527 static void
528 confirm_or_drop_neighbour (void *cls,
529                            const struct GNUNET_PeerIdentity *peer,
530                            int allowed)
531 {
532   if (GNUNET_OK == allowed)
533     return; /* we're done */
534   GNUNET_STATISTICS_update (GST_stats,
535                             gettext_noop ("# disconnects due to blacklist"),
536                             1,
537                             GNUNET_NO);
538   GST_neighbours_force_disconnect (peer);
539 }
540
541
542 /**
543  * Closure for 'test_connection_ok'.
544  */
545 struct TestConnectionContext
546 {
547   /**
548    * Is this the first neighbour we're checking?
549    */ 
550   int first;
551
552   /**
553    * Handle to the blacklisting client we need to ask.
554    */
555   struct Blacklisters *bl;
556 };
557
558
559 /**
560  * Test if an existing connection is still acceptable given a new
561  * blacklisting client.
562  *
563  * @param cls the 'struct TestConnectionContest'
564  * @param pid neighbour's identity
565  */
566 static void
567 test_connection_ok (void *cls,
568                     const struct GNUNET_PeerIdentity *neighbour)
569 {
570   struct TestConnectionContext *tcc = cls;
571   struct BlacklistCheck *bc;
572
573   bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
574   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
575   bc->peer = *neighbour;
576   bc->cont = &confirm_or_drop_neighbour;
577   bc->cont_cls = NULL;
578   bc->bl_pos = tcc->bl;
579   if (GNUNET_YES == tcc->first)
580     { 
581       /* all would wait for the same client, no need to
582          create more than just the first task right now */
583       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
584                                            bc);
585       tcc->first = GNUNET_NO;
586     }
587 }
588
589
590
591 /**
592  * Initialize a blacklisting client.  We got a blacklist-init
593  * message from this client, add him to the list of clients
594  * to query for blacklisting.
595  *
596  * @param cls unused
597  * @param client the client
598  * @param message the blacklist-init message that was sent
599  */
600 void
601 GST_blacklist_handle_init (void *cls,
602                            struct GNUNET_SERVER_Client *client,
603                            const struct GNUNET_MessageHeader *message)
604 {
605   struct Blacklisters *bl;
606   struct TestConnectionContext tcc;
607
608   bl = bl_head;
609   while (bl != NULL)
610     {
611       if (bl->client == client)
612         {
613           GNUNET_break (0);
614           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
615           return;
616         }
617       bl = bl->next;
618     }
619   bl = GNUNET_malloc (sizeof (struct Blacklisters));
620   bl->client = client;
621   GNUNET_SERVER_client_keep (client);
622   GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
623
624   /* confirm that all existing connections are OK! */
625   tcc.bl = bl;
626   tcc.first = GNUNET_YES;
627   GST_neighbours_iterate (&test_connection_ok,
628                           &tcc);
629 }
630
631
632 /**
633  * A blacklisting client has sent us reply. Process it.
634  *
635  * @param cls unused
636  * @param client the client
637  * @param message the blacklist-init message that was sent
638  */
639 void
640 GST_blacklist_handle_reply (void *cls,
641                             struct GNUNET_SERVER_Client *client,
642                             const struct GNUNET_MessageHeader *message)
643 {
644   const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
645   struct Blacklisters *bl;
646   struct BlacklistCheck *bc;
647
648   bl = bl_head;
649   while ( (bl != NULL) &&
650           (bl->client != client) )
651     bl = bl->next;
652   if (bl == NULL)
653     {
654 #if DEBUG_TRANSPORT
655       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
656                   "Blacklist client disconnected\n");
657 #endif
658       /* FIXME: other error handling here!? */
659       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
660       return;
661     }
662   bc = bl->bc;
663   bl->bc = NULL;
664   if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
665     {
666 #if DEBUG_TRANSPORT
667       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
668                   "Blacklist check failed, peer not allowed\n");
669 #endif
670       bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
671       GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
672       GNUNET_free (bc);
673     }
674   else
675     {
676 #if DEBUG_TRANSPORT
677       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
678                   "Blacklist check succeeded, continuing with checks\n");
679 #endif
680       bc->bl_pos = bc->bl_pos->next;
681       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
682                                            bc);
683     }
684   /* check if any other bc's are waiting for this blacklister */
685   bc = bc_head;
686   while (bc != NULL)
687     {
688       if ( (bc->bl_pos == bl) &&
689            (GNUNET_SCHEDULER_NO_TASK == bc->task) )
690         bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
691                                              bc);
692       bc = bc->next;
693     }
694 }
695
696
697 /**
698  * Add the given peer to the blacklist (for the given transport).
699  * 
700  * @param peer peer to blacklist
701  * @param transport_name transport to blacklist for this peer, NULL for all
702  */
703 void
704 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
705                         const char *transport_name)
706 {
707 #if DEBUG_TRANSPORT
708   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709               "Adding peer `%s' with plugin `%s' to blacklist\n",
710               GNUNET_i2s (peer),
711               transport_name);
712 #endif
713   if (blacklist == NULL)
714     blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);
715   GNUNET_CONTAINER_multihashmap_put (blacklist,
716                                      &peer->hashPubKey,
717                                      GNUNET_strdup (transport_name),
718                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
719 }
720
721
722 /**
723  * Test if the given blacklist entry matches.  If so,
724  * abort the iteration.
725  *
726  * @param cls the transport name to match (const char*)
727  * @param key the key (unused)
728  * @param value the 'char *' (name of a blacklisted transport)
729  * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
730  */
731 static int
732 test_blacklisted (void *cls,
733                   const GNUNET_HashCode *key,
734                   void *value)
735 {
736   const char *transport_name = cls;
737   char *be = value;
738
739   if (0 == strcmp (transport_name,
740                    be))
741     return GNUNET_NO; /* abort iteration! */
742   return GNUNET_OK;
743 }
744
745
746 /**
747  * Test if a peer/transport combination is blacklisted.
748  *
749  * @param peer the identity of the peer to test
750  * @param transport_name name of the transport to test, never NULL
751  * @param cont function to call with result
752  * @param cont_cls closure for 'cont'
753  */
754 void
755 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
756                             const char *transport_name,
757                             GST_BlacklistTestContinuation cont,
758                             void *cont_cls)
759 {
760   struct BlacklistCheck *bc;
761   
762   if ( (blacklist != NULL) &&
763        (GNUNET_SYSERR ==
764         GNUNET_CONTAINER_multihashmap_get_multiple (blacklist,
765                                                     &peer->hashPubKey,
766                                                     &test_blacklisted,
767                                                     (void*) transport_name)) )
768     {
769       /* disallowed by config, disapprove instantly */
770       GNUNET_STATISTICS_update (GST_stats,
771                                 gettext_noop ("# disconnects due to blacklist"),
772                                 1,
773                                 GNUNET_NO);
774       if (cont != NULL)
775         cont (cont_cls, peer, GNUNET_NO);
776       return;
777     }
778
779   if (bl_head == NULL)
780     {
781       /* no blacklist clients, approve instantly */
782       if (cont != NULL)
783         cont (cont_cls, peer, GNUNET_OK);
784       return;
785     }
786
787   /* need to query blacklist clients */
788   bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
789   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
790   bc->peer = *peer;
791   bc->cont = cont;
792   bc->cont_cls = cont_cls;
793   bc->bl_pos = bl_head;
794   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
795                                        bc);
796 }
797                                                  
798
799
800 /* end of file gnunet-service-transport_blacklist.c */