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