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