refactor DHT for new service API
[oweals/gnunet.git] / src / revocation / gnunet-service-revocation.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2013, 2014, 2016 GNUnet e.V.
4
5   GNUnet is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public Licerevocation 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 Licerevocation for more details.
14
15   You should have received a copy of the GNU General Public Licerevocation
16   along with GNUnet; see the file COPYING.  If not, write to the
17   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18   Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @file revocation/gnunet-service-revocation.c
23  * @brief key revocation service
24  * @author Christian Grothoff
25  *
26  * The purpose of this service is to allow users to permanently revoke
27  * (compromised) keys.  This is done by flooding the network with the
28  * revocation requests.  To reduce the attack potential offered by such
29  * flooding, revocations must include a proof of work.  We use the
30  * set service for efficiently computing the union of revocations of
31  * peers that connect.
32  *
33  * TODO:
34  * - optimization: avoid sending revocation back to peer that we got it from;
35  * - optimization: have randomized delay in sending revocations to other peers
36  *                 to make it rare to traverse each link twice (NSE-style)
37  */
38 #include "platform.h"
39 #include <math.h>
40 #include "gnunet_util_lib.h"
41 #include "gnunet_constants.h"
42 #include "gnunet_protocols.h"
43 #include "gnunet_signatures.h"
44 #include "gnunet_statistics_service.h"
45 #include "gnunet_core_service.h"
46 #include "gnunet_revocation_service.h"
47 #include "gnunet_set_service.h"
48 #include "revocation.h"
49 #include <gcrypt.h>
50
51
52 /**
53  * Per-peer information.
54  */
55 struct PeerEntry
56 {
57
58   /**
59    * Queue for sending messages to this peer.
60    */
61   struct GNUNET_MQ_Handle *mq;
62
63   /**
64    * What is the identity of the peer?
65    */
66   struct GNUNET_PeerIdentity id;
67
68   /**
69    * Tasked used to trigger the set union operation.
70    */
71   struct GNUNET_SCHEDULER_Task *transmit_task;
72
73   /**
74    * Handle to active set union operation (over revocation sets).
75    */
76   struct GNUNET_SET_OperationHandle *so;
77
78 };
79
80
81 /**
82  * Set from all revocations known to us.
83  */
84 static struct GNUNET_SET_Handle *revocation_set;
85
86 /**
87  * Hash map with all revoked keys, maps the hash of the public key
88  * to the respective `struct RevokeMessage`.
89  */
90 static struct GNUNET_CONTAINER_MultiHashMap *revocation_map;
91
92 /**
93  * Handle to our current configuration.
94  */
95 static const struct GNUNET_CONFIGURATION_Handle *cfg;
96
97 /**
98  * Handle to the statistics service.
99  */
100 static struct GNUNET_STATISTICS_Handle *stats;
101
102 /**
103  * Handle to the core service (for flooding)
104  */
105 static struct GNUNET_CORE_Handle *core_api;
106
107 /**
108  * Map of all connected peers.
109  */
110 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
111
112 /**
113  * The peer identity of this peer.
114  */
115 static struct GNUNET_PeerIdentity my_identity;
116
117 /**
118  * File handle for the revocation database.
119  */
120 static struct GNUNET_DISK_FileHandle *revocation_db;
121
122 /**
123  * Handle for us listening to incoming revocation set union requests.
124  */
125 static struct GNUNET_SET_ListenHandle *revocation_union_listen_handle;
126
127 /**
128  * Amount of work required (W-bit collisions) for REVOCATION proofs, in collision-bits.
129  */
130 static unsigned long long revocation_work_required;
131
132 /**
133  * Our application ID for set union operations.  Must be the
134  * same for all (compatible) peers.
135  */
136 static struct GNUNET_HashCode revocation_set_union_app_id;
137
138
139 /**
140  * Create a new PeerEntry and add it to the peers multipeermap.
141  *
142  * @param peer the peer identity
143  * @return a pointer to the new PeerEntry
144  */
145 static struct PeerEntry *
146 new_peer_entry(const struct GNUNET_PeerIdentity *peer)
147 {
148   struct PeerEntry *peer_entry;
149
150   peer_entry = GNUNET_new (struct PeerEntry);
151   peer_entry->id = *peer;
152   GNUNET_assert (GNUNET_OK ==
153                  GNUNET_CONTAINER_multipeermap_put (peers,
154                                                     &peer_entry->id,
155                                                     peer_entry,
156                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
157   return peer_entry;
158 }
159
160
161 /**
162  * An revoke message has been received, check that it is well-formed.
163  *
164  * @param rm the message to verify
165  * @return #GNUNET_YES if the message is verified
166  *         #GNUNET_NO if the key/signature don't verify
167  */
168 static int
169 verify_revoke_message (const struct RevokeMessage *rm)
170 {
171   if (GNUNET_YES !=
172       GNUNET_REVOCATION_check_pow (&rm->public_key,
173                                    rm->proof_of_work,
174                                    (unsigned int) revocation_work_required))
175   {
176     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
177                 "Proof of work invalid!\n");
178     GNUNET_break_op (0);
179     return GNUNET_NO;
180   }
181   if (GNUNET_OK !=
182       GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_REVOCATION,
183                                 &rm->purpose,
184                                 &rm->signature,
185                                 &rm->public_key))
186   {
187     GNUNET_break_op (0);
188     return GNUNET_NO;
189   }
190   return GNUNET_YES;
191 }
192
193
194 /**
195  * Handle client connecting to the service.
196  *
197  * @param cls NULL
198  * @param client the new client
199  * @param mq the message queue of @a client
200  * @return @a client
201  */
202 static void *
203 client_connect_cb (void *cls,
204                    struct GNUNET_SERVICE_Client *client,
205                    struct GNUNET_MQ_Handle *mq)
206 {
207   return client;
208 }
209
210
211 /**
212  * Handle client connecting to the service.
213  *
214  * @param cls NULL
215  * @param client the new client
216  * @param app_cls must alias @a client
217  */
218 static void 
219 client_disconnect_cb (void *cls,
220                       struct GNUNET_SERVICE_Client *client,
221                       void *app_cls)
222 {
223   GNUNET_assert (client == app_cls);
224 }
225
226
227 /**
228  * Handle QUERY message from client.
229  *
230  * @param cls client who sent the message
231  * @param qm the message received
232  */
233 static void
234 handle_query_message (void *cls,
235                       const struct QueryMessage *qm)
236 {
237   struct GNUNET_SERVICE_Client *client = cls;
238   struct GNUNET_MQ_Envelope *env;
239   struct QueryResponseMessage *qrm;
240   struct GNUNET_HashCode hc;
241   int res;
242
243   GNUNET_CRYPTO_hash (&qm->key,
244                       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
245                       &hc);
246   res = GNUNET_CONTAINER_multihashmap_contains (revocation_map,
247                                                 &hc);
248   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
249               (GNUNET_NO == res)
250               ? "Received revocation check for valid key `%s' from client\n"
251               : "Received revocation check for revoked key `%s' from client\n",
252               GNUNET_h2s (&hc));
253   env = GNUNET_MQ_msg (qrm,
254                        GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE);
255   qrm->is_valid = htonl ((GNUNET_YES == res) ? GNUNET_NO : GNUNET_YES);
256   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
257                   env);
258   GNUNET_SERVICE_client_continue (client);
259 }
260
261
262 /**
263  * Flood the given revocation message to all neighbours.
264  *
265  * @param cls the `struct RevokeMessage` to flood
266  * @param target a neighbour
267  * @param value our `struct PeerEntry` for the neighbour
268  * @return #GNUNET_OK (continue to iterate)
269  */
270 static int
271 do_flood (void *cls,
272           const struct GNUNET_PeerIdentity *target,
273           void *value)
274 {
275   const struct RevokeMessage *rm = cls;
276   struct PeerEntry *pe = value;
277   struct GNUNET_MQ_Envelope *e;
278   struct RevokeMessage *cp;
279
280   if (NULL == pe->mq)
281     return GNUNET_OK; /* peer connected to us via SET,
282                          but we have no direct CORE
283                          connection for flooding */
284   e = GNUNET_MQ_msg (cp,
285                      GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE);
286   *cp = *rm;
287   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288               "Flooding revocation to `%s'\n",
289               GNUNET_i2s (target));
290   GNUNET_MQ_send (pe->mq,
291                   e);
292   return GNUNET_OK;
293 }
294
295
296 /**
297  * Publicize revocation message.   Stores the message locally in the
298  * database and passes it to all connected neighbours (and adds it to
299  * the set for future connections).
300  *
301  * @param rm message to publicize
302  * @return #GNUNET_OK on success, #GNUNET_NO if we encountered an error,
303  *         #GNUNET_SYSERR if the message was malformed
304  */
305 static int
306 publicize_rm (const struct RevokeMessage *rm)
307 {
308   struct RevokeMessage *cp;
309   struct GNUNET_HashCode hc;
310   struct GNUNET_SET_Element e;
311
312   GNUNET_CRYPTO_hash (&rm->public_key,
313                       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
314                       &hc);
315   if (GNUNET_YES ==
316       GNUNET_CONTAINER_multihashmap_contains (revocation_map,
317                                               &hc))
318   {
319     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
320                 "Duplicate revocation received from peer. Ignored.\n");
321     return GNUNET_OK;
322   }
323   if (GNUNET_OK !=
324       verify_revoke_message (rm))
325   {
326     GNUNET_break_op (0);
327     return GNUNET_SYSERR;
328   }
329   /* write to disk */
330   if (sizeof (struct RevokeMessage) !=
331       GNUNET_DISK_file_write (revocation_db,
332                               rm,
333                               sizeof (struct RevokeMessage)))
334   {
335     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
336                          "write");
337     return GNUNET_NO;
338   }
339   if (GNUNET_OK !=
340       GNUNET_DISK_file_sync (revocation_db))
341   {
342     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
343                          "sync");
344     return GNUNET_NO;
345   }
346   /* keep copy in memory */
347   cp = (struct RevokeMessage *) GNUNET_copy_message (&rm->header);
348   GNUNET_break (GNUNET_OK ==
349                 GNUNET_CONTAINER_multihashmap_put (revocation_map,
350                                                    &hc,
351                                                    cp,
352                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
353   /* add to set for future connections */
354   e.size = htons (rm->header.size);
355   e.element_type = 0;
356   e.data = rm;
357   if (GNUNET_OK !=
358       GNUNET_SET_add_element (revocation_set,
359                               &e,
360                               NULL,
361                               NULL))
362   {
363     GNUNET_break (0);
364     return GNUNET_OK;
365   }
366   else
367   {
368     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
369                 "Added revocation info to SET\n");
370   }
371   /* flood to neighbours */
372   GNUNET_CONTAINER_multipeermap_iterate (peers,
373                                          &do_flood,
374                                          cp);
375   return GNUNET_OK;
376 }
377
378
379 /**
380  * Handle REVOKE message from client.
381  *
382  * @param cls client who sent the message
383  * @param rm the message received
384  */
385 static void
386 handle_revoke_message (void *cls,
387                        const struct RevokeMessage *rm)
388 {
389   struct GNUNET_SERVICE_Client *client = cls;
390   struct GNUNET_MQ_Envelope *env;
391   struct RevocationResponseMessage *rrm;
392   int ret;
393
394   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395               "Received REVOKE message from client\n");
396   if (GNUNET_SYSERR == (ret = publicize_rm (rm)))
397   {
398     GNUNET_break_op (0);
399     GNUNET_SERVICE_client_drop (client);
400     return;
401   }
402   env = GNUNET_MQ_msg (rrm,
403                        GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE);
404   rrm->is_valid = htonl ((GNUNET_OK == ret) ? GNUNET_NO : GNUNET_YES);
405   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
406                   env);
407   GNUNET_SERVICE_client_continue (client);
408 }
409
410
411 /**
412  * Core handler for flooded revocation messages.
413  *
414  * @param cls closure unused
415  * @param rm revocation message
416  */
417 static void
418 handle_p2p_revoke (void *cls,
419                    const struct RevokeMessage *rm)
420 {
421   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422               "Received REVOKE message\n");
423   GNUNET_break_op (GNUNET_SYSERR !=
424                    publicize_rm (rm));
425 }
426
427
428 /**
429  * Callback for set operation results. Called for each element in the
430  * result set.  Each element contains a revocation, which we should
431  * validate and then add to our revocation list (and set).
432  *
433  * @param cls closure
434  * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK
435  * @param status see `enum GNUNET_SET_Status`
436  */
437 static void
438 add_revocation (void *cls,
439                 const struct GNUNET_SET_Element *element,
440                 enum GNUNET_SET_Status status)
441 {
442   struct PeerEntry *peer_entry = cls;
443   const struct RevokeMessage *rm;
444
445   switch (status)
446   {
447   case GNUNET_SET_STATUS_OK:
448     if (element->size != sizeof (struct RevokeMessage))
449     {
450       GNUNET_break_op (0);
451       return;
452     }
453     if (0 != element->element_type)
454     {
455       GNUNET_STATISTICS_update (stats,
456                                 gettext_noop ("# unsupported revocations received via set union"),
457                                 1, GNUNET_NO);
458       return;
459     }
460     rm = element->data;
461     (void) handle_p2p_revoke (NULL,
462                               rm);
463     GNUNET_STATISTICS_update (stats,
464                               gettext_noop ("# revocation messages received via set union"),
465                               1, GNUNET_NO);
466     break;
467   case GNUNET_SET_STATUS_FAILURE:
468     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
469                 _("Error computing revocation set union with %s\n"),
470                 GNUNET_i2s (&peer_entry->id));
471     peer_entry->so = NULL;
472     GNUNET_STATISTICS_update (stats,
473                               gettext_noop ("# revocation set unions failed"),
474                               1,
475                               GNUNET_NO);
476     break;
477   case GNUNET_SET_STATUS_HALF_DONE:
478     break;
479   case GNUNET_SET_STATUS_DONE:
480     peer_entry->so = NULL;
481     GNUNET_STATISTICS_update (stats,
482                               gettext_noop ("# revocation set unions completed"),
483                               1,
484                               GNUNET_NO);
485     break;
486   default:
487     GNUNET_break (0);
488     break;
489  }
490 }
491
492
493 /**
494  * The timeout for performing the set union has expired,
495  * run the set operation on the revocation certificates.
496  *
497  * @param cls NULL
498  */
499 static void
500 transmit_task_cb (void *cls)
501 {
502   struct PeerEntry *peer_entry = cls;
503
504   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
505               "Starting set exchange with peer `%s'\n",
506               GNUNET_i2s (&peer_entry->id));
507   peer_entry->transmit_task = NULL;
508   peer_entry->so = GNUNET_SET_prepare (&peer_entry->id,
509                                        &revocation_set_union_app_id,
510                                        NULL,
511                                        GNUNET_SET_RESULT_ADDED,
512                                        &add_revocation,
513                                        peer_entry);
514   if (GNUNET_OK !=
515       GNUNET_SET_commit (peer_entry->so,
516                          revocation_set))
517   {
518     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
519                 _("SET service crashed, terminating revocation service\n"));
520     GNUNET_SCHEDULER_shutdown ();
521     return;
522   }
523 }
524
525
526 /**
527  * Method called whenever a peer connects. Sets up the PeerEntry and
528  * schedules the initial revocation set exchange with this peer.
529  *
530  * @param cls closure
531  * @param peer peer identity this notification is about
532  */
533 static void *
534 handle_core_connect (void *cls,
535                      const struct GNUNET_PeerIdentity *peer,
536                      struct GNUNET_MQ_Handle *mq)
537 {
538   struct PeerEntry *peer_entry;
539   struct GNUNET_HashCode my_hash;
540   struct GNUNET_HashCode peer_hash;
541
542   if (0 == memcmp (peer,
543                    &my_identity,
544                    sizeof (my_identity)))
545   {
546     return NULL;
547   }
548
549   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
550               "Peer `%s' connected to us\n",
551               GNUNET_i2s (peer));
552   GNUNET_STATISTICS_update (stats,
553                             "# peers connected",
554                             1,
555                             GNUNET_NO);
556   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
557                                                   peer);
558   if (NULL != peer_entry)
559   {
560     /* This can happen if "core"'s notification is a tad late
561        and CADET+SET were faster and already produced a
562        #handle_revocation_union_request() for us to deal
563        with.  This should be rare, but isn't impossible. */
564     peer_entry->mq = mq;
565     return peer_entry;
566   }
567   peer_entry = new_peer_entry (peer);
568   peer_entry->mq = mq;
569   GNUNET_CRYPTO_hash (&my_identity,
570                       sizeof (my_identity),
571                       &my_hash);
572   GNUNET_CRYPTO_hash (peer,
573                       sizeof (*peer),
574                       &peer_hash);
575   if (0 < GNUNET_CRYPTO_hash_cmp (&my_hash,
576                                   &peer_hash))
577   {
578     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
579                 "Starting SET operation with peer `%s'\n",
580                 GNUNET_i2s (peer));
581     peer_entry->transmit_task =
582       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
583                                     &transmit_task_cb,
584                                     peer_entry);
585   }
586   return peer_entry;
587 }
588
589
590 /**
591  * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
592  * any pending transmission requests to that peer.
593  *
594  * @param cls closure
595  * @param peer peer identity this notification is about
596  * @param internal_cls our `struct PeerEntry` for this peer
597  */
598 static void
599 handle_core_disconnect (void *cls,
600                         const struct GNUNET_PeerIdentity *peer,
601                         void *internal_cls)
602 {
603   struct PeerEntry *peer_entry = internal_cls;
604   
605   if (0 == memcmp (peer,
606                    &my_identity,
607                    sizeof (my_identity)))
608     return;
609   GNUNET_assert (NULL != peer_entry);  
610   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
611               "Peer `%s' disconnected from us\n",
612               GNUNET_i2s (peer));
613   GNUNET_assert (GNUNET_YES ==
614                  GNUNET_CONTAINER_multipeermap_remove (peers,
615                                                        peer,
616                                                        peer_entry));
617   if (NULL != peer_entry->transmit_task)
618   {
619     GNUNET_SCHEDULER_cancel (peer_entry->transmit_task);
620     peer_entry->transmit_task = NULL;
621   }
622   if (NULL != peer_entry->so)
623   {
624     GNUNET_SET_operation_cancel (peer_entry->so);
625     peer_entry->so = NULL;
626   }
627   GNUNET_free (peer_entry);
628   GNUNET_STATISTICS_update (stats,
629                             "# peers connected",
630                             -1,
631                             GNUNET_NO);
632 }
633
634
635 /**
636  * Free all values in a hash map.
637  *
638  * @param cls NULL
639  * @param key the key
640  * @param value value to free
641  * @return #GNUNET_OK (continue to iterate)
642  */
643 static int
644 free_entry (void *cls,
645             const struct GNUNET_HashCode *key,
646             void *value)
647 {
648   GNUNET_free (value);
649   return GNUNET_OK;
650 }
651
652
653 /**
654  * Task run during shutdown.
655  *
656  * @param cls unused
657  */
658 static void
659 shutdown_task (void *cls)
660 {
661   if (NULL != revocation_set)
662   {
663     GNUNET_SET_destroy (revocation_set);
664     revocation_set = NULL;
665   }
666   if (NULL != revocation_union_listen_handle)
667   {
668     GNUNET_SET_listen_cancel (revocation_union_listen_handle);
669     revocation_union_listen_handle = NULL;
670   }
671   if (NULL != core_api)
672   {
673     GNUNET_CORE_disconnecT (core_api);
674     core_api = NULL;
675   }
676   if (NULL != stats)
677   {
678     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
679     stats = NULL;
680   }
681   if (NULL != peers)
682   {
683     GNUNET_CONTAINER_multipeermap_destroy (peers);
684     peers = NULL;
685   }
686   if (NULL != revocation_db)
687   {
688     GNUNET_DISK_file_close (revocation_db);
689     revocation_db = NULL;
690   }
691   GNUNET_CONTAINER_multihashmap_iterate (revocation_map,
692                                          &free_entry,
693                                          NULL);
694   GNUNET_CONTAINER_multihashmap_destroy (revocation_map);
695 }
696
697
698 /**
699  * Called on core init/fail.
700  *
701  * @param cls service closure
702  * @param identity the public identity of this peer
703  */
704 static void
705 core_init (void *cls,
706            const struct GNUNET_PeerIdentity *identity)
707 {
708   if (NULL == identity)
709   {
710     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
711                 "Connection to core FAILED!\n");
712     GNUNET_SCHEDULER_shutdown ();
713     return;
714   }
715   my_identity = *identity;
716 }
717
718
719 /**
720  * Called when another peer wants to do a set operation with the
721  * local peer. If a listen error occurs, the 'request' is NULL.
722  *
723  * @param cls closure
724  * @param other_peer the other peer
725  * @param context_msg message with application specific information from
726  *        the other peer
727  * @param request request from the other peer (never NULL), use GNUNET_SET_accept()
728  *        to accept it, otherwise the request will be refused
729  *        Note that we can't just return value from the listen callback,
730  *        as it is also necessary to specify the set we want to do the
731  *        operation with, whith sometimes can be derived from the context
732  *        message. It's necessary to specify the timeout.
733  */
734 static void
735 handle_revocation_union_request (void *cls,
736                                  const struct GNUNET_PeerIdentity *other_peer,
737                                  const struct GNUNET_MessageHeader *context_msg,
738                                  struct GNUNET_SET_Request *request)
739 {
740   struct PeerEntry *peer_entry;
741
742   if (NULL == request)
743   {
744     GNUNET_break (0);
745     return;
746   }
747   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748               "Received set exchange request from peer `%s'\n",
749               GNUNET_i2s (other_peer));
750   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
751                                                   other_peer);
752   if (NULL == peer_entry)
753   {
754     peer_entry = new_peer_entry (other_peer);
755   }
756   peer_entry->so = GNUNET_SET_accept (request,
757                                       GNUNET_SET_RESULT_ADDED,
758                                       &add_revocation,
759                                       peer_entry);
760   if (GNUNET_OK !=
761       GNUNET_SET_commit (peer_entry->so,
762                          revocation_set))
763   {
764     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
765                 _("SET service crashed, terminating revocation service\n"));
766     GNUNET_SCHEDULER_shutdown ();
767     return;
768   }
769 }
770
771
772 /**
773  * Handle network size estimate clients.
774  *
775  * @param cls closure
776  * @param server the initialized server
777  * @param c configuration to use
778  */
779 static void
780 run (void *cls,
781      const struct GNUNET_CONFIGURATION_Handle *c,
782      struct GNUNET_SERVICE_Handle *service)     
783 {
784   struct GNUNET_MQ_MessageHandler core_handlers[] = {
785     GNUNET_MQ_hd_fixed_size (p2p_revoke,
786                              GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
787                              struct RevokeMessage,
788                              NULL),
789     GNUNET_MQ_handler_end ()
790   };
791   char *fn;
792   uint64_t left;
793   struct RevokeMessage *rm;
794   struct GNUNET_HashCode hc;
795
796   GNUNET_CRYPTO_hash ("revocation-set-union-application-id",
797                       strlen ("revocation-set-union-application-id"),
798                       &revocation_set_union_app_id);
799   if (GNUNET_OK !=
800       GNUNET_CONFIGURATION_get_value_filename (c,
801                                                "REVOCATION",
802                                                "DATABASE",
803                                                &fn))
804   {
805     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
806                                "REVOCATION",
807                                "DATABASE");
808     GNUNET_SCHEDULER_shutdown ();
809     return;
810   }
811   cfg = c;
812   revocation_map = GNUNET_CONTAINER_multihashmap_create (16,
813                                                          GNUNET_NO);
814   if (GNUNET_OK !=
815       GNUNET_CONFIGURATION_get_value_number (cfg,
816                                              "REVOCATION",
817                                              "WORKBITS",
818                                              &revocation_work_required))
819   {
820     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
821                                "REVOCATION",
822                                "WORKBITS");
823     GNUNET_SCHEDULER_shutdown ();
824     GNUNET_free (fn);
825     return;
826   }
827   if (revocation_work_required >= sizeof (struct GNUNET_HashCode) * 8)
828   {
829     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
830                                "REVOCATION",
831                                "WORKBITS",
832                                _("Value is too large.\n"));
833     GNUNET_SCHEDULER_shutdown ();
834     GNUNET_free (fn);
835     return;
836   }
837   revocation_set = GNUNET_SET_create (cfg,
838                                       GNUNET_SET_OPERATION_UNION);
839   revocation_union_listen_handle
840     = GNUNET_SET_listen (cfg,
841                          GNUNET_SET_OPERATION_UNION,
842                          &revocation_set_union_app_id,
843                          &handle_revocation_union_request,
844                          NULL);
845   revocation_db = GNUNET_DISK_file_open (fn,
846                                          GNUNET_DISK_OPEN_READWRITE |
847                                          GNUNET_DISK_OPEN_CREATE,
848                                          GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE |
849                                          GNUNET_DISK_PERM_GROUP_READ |
850                                          GNUNET_DISK_PERM_OTHER_READ);
851   if (NULL == revocation_db)
852   {
853     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
854                                "REVOCATION",
855                                "DATABASE",
856                                _("Could not open revocation database file!"));
857     GNUNET_SCHEDULER_shutdown ();
858     GNUNET_free (fn);
859     return;
860   }
861   if (GNUNET_OK !=
862       GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES))
863     left = 0;
864   while (left > sizeof (struct RevokeMessage))
865   {
866     rm = GNUNET_new (struct RevokeMessage);
867     if (sizeof (struct RevokeMessage) !=
868         GNUNET_DISK_file_read (revocation_db,
869                                rm,
870                                sizeof (struct RevokeMessage)))
871     {
872       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
873                                 "read",
874                                 fn);
875       GNUNET_free (rm);
876       GNUNET_SCHEDULER_shutdown ();
877       GNUNET_free (fn);
878       return;
879     }
880     GNUNET_break (0 == ntohl (rm->reserved));
881     GNUNET_CRYPTO_hash (&rm->public_key,
882                         sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
883                         &hc);
884     GNUNET_break (GNUNET_OK ==
885                   GNUNET_CONTAINER_multihashmap_put (revocation_map,
886                                                      &hc,
887                                                      rm,
888                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
889   }
890   GNUNET_free (fn);
891
892   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
893                                  NULL);
894   peers = GNUNET_CONTAINER_multipeermap_create (128,
895                                                 GNUNET_YES);
896   /* Connect to core service and register core handlers */
897   core_api = GNUNET_CORE_connecT (cfg,   /* Main configuration */
898                                   NULL,       /* Closure passed to functions */
899                                   &core_init,    /* Call core_init once connected */
900                                   &handle_core_connect,  /* Handle connects */
901                                   &handle_core_disconnect,       /* Handle disconnects */
902                                   core_handlers);        /* Register these handlers */
903   if (NULL == core_api)
904   {
905     GNUNET_SCHEDULER_shutdown ();
906     return;
907   }
908   stats = GNUNET_STATISTICS_create ("revocation",
909                                     cfg);
910 }
911
912
913 /**
914  * Define "main" method using service macro.
915  */
916 GNUNET_SERVICE_MAIN
917 ("revocation",
918  GNUNET_SERVICE_OPTION_NONE,
919  &run,
920  &client_connect_cb,
921  &client_disconnect_cb,
922  NULL,
923  GNUNET_MQ_hd_fixed_size (query_message,
924                           GNUNET_MESSAGE_TYPE_REVOCATION_QUERY,
925                           struct QueryMessage,
926                           NULL),
927  GNUNET_MQ_hd_fixed_size (revoke_message,
928                           GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
929                           struct RevokeMessage,
930                           NULL),
931  GNUNET_MQ_handler_end ());
932
933
934 #if defined(LINUX) && defined(__GLIBC__)
935 #include <malloc.h>
936
937 /**
938  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
939  */
940 void __attribute__ ((constructor))
941 GNUNET_REVOCATION_memory_init ()
942 {
943   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
944   mallopt (M_TOP_PAD, 1 * 1024);
945   malloc_trim (0);
946 }
947
948 #endif
949
950
951 /* end of gnunet-service-revocation.c */