stuff
[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  * @param ats performance data
566  * @param ats_count number of entries in ats (excluding 0-termination)
567  */
568 static void
569 test_connection_ok (void *cls,
570                     const struct GNUNET_PeerIdentity *neighbour,
571                     const struct GNUNET_TRANSPORT_ATS_Information *ats,
572                     uint32_t ats_count)
573 {
574   struct TestConnectionContext *tcc = cls;
575   struct BlacklistCheck *bc;
576
577   bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
578   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
579   bc->peer = *neighbour;
580   bc->cont = &confirm_or_drop_neighbour;
581   bc->cont_cls = NULL;
582   bc->bl_pos = tcc->bl;
583   if (GNUNET_YES == tcc->first)
584     { 
585       /* all would wait for the same client, no need to
586          create more than just the first task right now */
587       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
588                                            bc);
589       tcc->first = GNUNET_NO;
590     }
591 }
592
593
594
595 /**
596  * Initialize a blacklisting client.  We got a blacklist-init
597  * message from this client, add him to the list of clients
598  * to query for blacklisting.
599  *
600  * @param cls unused
601  * @param client the client
602  * @param message the blacklist-init message that was sent
603  */
604 void
605 GST_blacklist_handle_init (void *cls,
606                            struct GNUNET_SERVER_Client *client,
607                            const struct GNUNET_MessageHeader *message)
608 {
609   struct Blacklisters *bl;
610   struct TestConnectionContext tcc;
611
612   bl = bl_head;
613   while (bl != NULL)
614     {
615       if (bl->client == client)
616         {
617           GNUNET_break (0);
618           GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
619           return;
620         }
621       bl = bl->next;
622     }
623   bl = GNUNET_malloc (sizeof (struct Blacklisters));
624   bl->client = client;
625   GNUNET_SERVER_client_keep (client);
626   GNUNET_CONTAINER_DLL_insert_after (bl_head, bl_tail, bl_tail, bl);
627
628   /* confirm that all existing connections are OK! */
629   tcc.bl = bl;
630   tcc.first = GNUNET_YES;
631   GST_neighbours_iterate (&test_connection_ok,
632                           &tcc);
633 }
634
635
636 /**
637  * A blacklisting client has sent us reply. Process it.
638  *
639  * @param cls unused
640  * @param client the client
641  * @param message the blacklist-init message that was sent
642  */
643 void
644 GST_blacklist_handle_reply (void *cls,
645                             struct GNUNET_SERVER_Client *client,
646                             const struct GNUNET_MessageHeader *message)
647 {
648   const struct BlacklistMessage *msg = (const struct BlacklistMessage*) message;
649   struct Blacklisters *bl;
650   struct BlacklistCheck *bc;
651
652   bl = bl_head;
653   while ( (bl != NULL) &&
654           (bl->client != client) )
655     bl = bl->next;
656   if (bl == NULL)
657     {
658 #if DEBUG_TRANSPORT
659       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660                   "Blacklist client disconnected\n");
661 #endif
662       /* FIXME: other error handling here!? */
663       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
664       return;
665     }
666   bc = bl->bc;
667   bl->bc = NULL;
668   if (ntohl (msg->is_allowed) == GNUNET_SYSERR)
669     {
670 #if DEBUG_TRANSPORT
671       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
672                   "Blacklist check failed, peer not allowed\n");
673 #endif
674       bc->cont (bc->cont_cls, &bc->peer, GNUNET_NO);
675       GNUNET_CONTAINER_DLL_remove (bc_head, bc_tail, bc);
676       GNUNET_free (bc);
677     }
678   else
679     {
680 #if DEBUG_TRANSPORT
681       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
682                   "Blacklist check succeeded, continuing with checks\n");
683 #endif
684       bc->bl_pos = bc->bl_pos->next;
685       bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
686                                            bc);
687     }
688   /* check if any other bc's are waiting for this blacklister */
689   bc = bc_head;
690   while (bc != NULL)
691     {
692       if ( (bc->bl_pos == bl) &&
693            (GNUNET_SCHEDULER_NO_TASK == bc->task) )
694         bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
695                                              bc);
696       bc = bc->next;
697     }
698 }
699
700
701 /**
702  * Add the given peer to the blacklist (for the given transport).
703  * 
704  * @param peer peer to blacklist
705  * @param transport_name transport to blacklist for this peer, NULL for all
706  */
707 void
708 GST_blacklist_add_peer (const struct GNUNET_PeerIdentity *peer,
709                         const char *transport_name)
710 {
711 #if DEBUG_TRANSPORT
712   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713               "Adding peer `%s' with plugin `%s' to blacklist\n",
714               GNUNET_i2s (peer),
715               transport_name);
716 #endif
717   if (blacklist == NULL)
718     blacklist = GNUNET_CONTAINER_multihashmap_create(TRANSPORT_BLACKLIST_HT_SIZE);
719   GNUNET_CONTAINER_multihashmap_put (blacklist,
720                                      &peer->hashPubKey,
721                                      GNUNET_strdup (transport_name),
722                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
723 }
724
725
726 /**
727  * Test if the given blacklist entry matches.  If so,
728  * abort the iteration.
729  *
730  * @param cls the transport name to match (const char*)
731  * @param key the key (unused)
732  * @param value the 'char *' (name of a blacklisted transport)
733  * @return GNUNET_OK if the entry does not match, GNUNET_NO if it matches
734  */
735 static int
736 test_blacklisted (void *cls,
737                   const GNUNET_HashCode *key,
738                   void *value)
739 {
740   const char *transport_name = cls;
741   char *be = value;
742
743   if (0 == strcmp (transport_name,
744                    be))
745     return GNUNET_NO; /* abort iteration! */
746   return GNUNET_OK;
747 }
748
749
750 /**
751  * Test if a peer/transport combination is blacklisted.
752  *
753  * @param peer the identity of the peer to test
754  * @param transport_name name of the transport to test, never NULL
755  * @param cont function to call with result
756  * @param cont_cls closure for 'cont'
757  */
758 void
759 GST_blacklist_test_allowed (const struct GNUNET_PeerIdentity *peer,
760                             const char *transport_name,
761                             GST_BlacklistTestContinuation cont,
762                             void *cont_cls)
763 {
764   struct BlacklistCheck *bc;
765   
766   if ( (blacklist != NULL) &&
767        (GNUNET_SYSERR ==
768         GNUNET_CONTAINER_multihashmap_get_multiple (blacklist,
769                                                     &peer->hashPubKey,
770                                                     &test_blacklisted,
771                                                     (void*) transport_name)) )
772     {
773       /* disallowed by config, disapprove instantly */
774       GNUNET_STATISTICS_update (GST_stats,
775                                 gettext_noop ("# disconnects due to blacklist"),
776                                 1,
777                                 GNUNET_NO);
778       if (cont != NULL)
779         cont (cont_cls, peer, GNUNET_NO);
780       return;
781     }
782
783   if (bl_head == NULL)
784     {
785       /* no blacklist clients, approve instantly */
786       if (cont != NULL)
787         cont (cont_cls, peer, GNUNET_OK);
788       return;
789     }
790
791   /* need to query blacklist clients */
792   bc = GNUNET_malloc (sizeof (struct BlacklistCheck));
793   GNUNET_CONTAINER_DLL_insert (bc_head, bc_tail, bc);
794   bc->peer = *peer;
795   bc->cont = cont;
796   bc->cont_cls = cont_cls;
797   bc->bl_pos = bl_head;
798   bc->task = GNUNET_SCHEDULER_add_now (&do_blacklist_check,
799                                        bc);
800 }
801                                                  
802
803
804 /* end of file gnunet-service-transport_blacklist.c */