-remove debug message
[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      SPDX-License-Identifier: AGPL3.0-or-later
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    * 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  * 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  * Length of an expiration expoch
133  */
134 static struct GNUNET_TIME_Relative epoch_duration;
135
136 /**
137  * Our application ID for set union operations.  Must be the
138  * same for all (compatible) peers.
139  */
140 static struct GNUNET_HashCode revocation_set_union_app_id;
141
142
143 /**
144  * Create a new PeerEntry and add it to the peers multipeermap.
145  *
146  * @param peer the peer identity
147  * @return a pointer to the new PeerEntry
148  */
149 static struct PeerEntry *
150 new_peer_entry (const struct GNUNET_PeerIdentity *peer)
151 {
152   struct PeerEntry *peer_entry;
153
154   peer_entry = GNUNET_new (struct PeerEntry);
155   peer_entry->id = *peer;
156   GNUNET_assert (GNUNET_OK ==
157                  GNUNET_CONTAINER_multipeermap_put (peers,
158                                                     &peer_entry->id,
159                                                     peer_entry,
160                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
161   return peer_entry;
162 }
163
164
165 /**
166  * An revoke message has been received, check that it is well-formed.
167  *
168  * @param rm the message to verify
169  * @return #GNUNET_YES if the message is verified
170  *         #GNUNET_NO if the key/signature don't verify
171  */
172 static int
173 verify_revoke_message (const struct RevokeMessage *rm)
174 {
175   if (GNUNET_YES != GNUNET_REVOCATION_check_pow (&rm->proof_of_work,
176                                                  (unsigned
177                                                   int) revocation_work_required,
178                                                  epoch_duration))
179   {
180     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
181                 "Proof of work invalid!\n");
182     GNUNET_break_op (0);
183     return GNUNET_NO;
184   }
185   return GNUNET_YES;
186 }
187
188
189 /**
190  * Handle client connecting to the service.
191  *
192  * @param cls NULL
193  * @param client the new client
194  * @param mq the message queue of @a client
195  * @return @a client
196  */
197 static void *
198 client_connect_cb (void *cls,
199                    struct GNUNET_SERVICE_Client *client,
200                    struct GNUNET_MQ_Handle *mq)
201 {
202   return client;
203 }
204
205
206 /**
207  * Handle client connecting to the service.
208  *
209  * @param cls NULL
210  * @param client the new client
211  * @param app_cls must alias @a client
212  */
213 static void
214 client_disconnect_cb (void *cls,
215                       struct GNUNET_SERVICE_Client *client,
216                       void *app_cls)
217 {
218   GNUNET_assert (client == app_cls);
219 }
220
221
222 /**
223  * Handle QUERY message from client.
224  *
225  * @param cls client who sent the message
226  * @param qm the message received
227  */
228 static void
229 handle_query_message (void *cls,
230                       const struct QueryMessage *qm)
231 {
232   struct GNUNET_SERVICE_Client *client = cls;
233   struct GNUNET_MQ_Envelope *env;
234   struct QueryResponseMessage *qrm;
235   struct GNUNET_HashCode hc;
236   int res;
237
238   GNUNET_CRYPTO_hash (&qm->key,
239                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
240                       &hc);
241   res = GNUNET_CONTAINER_multihashmap_contains (revocation_map,
242                                                 &hc);
243   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
244               (GNUNET_NO == res)
245               ? "Received revocation check for valid key `%s' from client\n"
246               : "Received revocation check for revoked key `%s' from client\n",
247               GNUNET_h2s (&hc));
248   env = GNUNET_MQ_msg (qrm,
249                        GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE);
250   qrm->is_valid = htonl ((GNUNET_YES == res) ? GNUNET_NO : GNUNET_YES);
251   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
252                   env);
253   GNUNET_SERVICE_client_continue (client);
254 }
255
256
257 /**
258  * Flood the given revocation message to all neighbours.
259  *
260  * @param cls the `struct RevokeMessage` to flood
261  * @param target a neighbour
262  * @param value our `struct PeerEntry` for the neighbour
263  * @return #GNUNET_OK (continue to iterate)
264  */
265 static int
266 do_flood (void *cls,
267           const struct GNUNET_PeerIdentity *target,
268           void *value)
269 {
270   const struct RevokeMessage *rm = cls;
271   struct PeerEntry *pe = value;
272   struct GNUNET_MQ_Envelope *e;
273   struct RevokeMessage *cp;
274
275   if (NULL == pe->mq)
276     return GNUNET_OK; /* peer connected to us via SET,
277                          but we have no direct CORE
278                          connection for flooding */
279   e = GNUNET_MQ_msg (cp,
280                      GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE);
281   *cp = *rm;
282   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
283               "Flooding revocation to `%s'\n",
284               GNUNET_i2s (target));
285   GNUNET_MQ_send (pe->mq,
286                   e);
287   return GNUNET_OK;
288 }
289
290
291 /**
292  * Publicize revocation message.   Stores the message locally in the
293  * database and passes it to all connected neighbours (and adds it to
294  * the set for future connections).
295  *
296  * @param rm message to publicize
297  * @return #GNUNET_OK on success, #GNUNET_NO if we encountered an error,
298  *         #GNUNET_SYSERR if the message was malformed
299  */
300 static int
301 publicize_rm (const struct RevokeMessage *rm)
302 {
303   struct RevokeMessage *cp;
304   struct GNUNET_HashCode hc;
305   struct GNUNET_SET_Element e;
306
307   GNUNET_CRYPTO_hash (&rm->proof_of_work.key,
308                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
309                       &hc);
310   if (GNUNET_YES ==
311       GNUNET_CONTAINER_multihashmap_contains (revocation_map,
312                                               &hc))
313   {
314     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315                 "Duplicate revocation received from peer. Ignored.\n");
316     return GNUNET_OK;
317   }
318   if (GNUNET_OK !=
319       verify_revoke_message (rm))
320   {
321     GNUNET_break_op (0);
322     return GNUNET_SYSERR;
323   }
324   /* write to disk */
325   if (sizeof(struct RevokeMessage) !=
326       GNUNET_DISK_file_write (revocation_db,
327                               rm,
328                               sizeof(struct RevokeMessage)))
329   {
330     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
331                          "write");
332     return GNUNET_NO;
333   }
334   if (GNUNET_OK !=
335       GNUNET_DISK_file_sync (revocation_db))
336   {
337     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
338                          "sync");
339     return GNUNET_NO;
340   }
341   /* keep copy in memory */
342   cp = (struct RevokeMessage *) GNUNET_copy_message (&rm->header);
343   GNUNET_break (GNUNET_OK ==
344                 GNUNET_CONTAINER_multihashmap_put (revocation_map,
345                                                    &hc,
346                                                    cp,
347                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
348   /* add to set for future connections */
349   e.size = htons (rm->header.size);
350   e.element_type = GNUNET_BLOCK_TYPE_REVOCATION;
351   e.data = rm;
352   if (GNUNET_OK !=
353       GNUNET_SET_add_element (revocation_set,
354                               &e,
355                               NULL,
356                               NULL))
357   {
358     GNUNET_break (0);
359     return GNUNET_OK;
360   }
361   else
362   {
363     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364                 "Added revocation info to SET\n");
365   }
366   /* flood to neighbours */
367   GNUNET_CONTAINER_multipeermap_iterate (peers,
368                                          &do_flood,
369                                          cp);
370   return GNUNET_OK;
371 }
372
373
374 /**
375  * Handle REVOKE message from client.
376  *
377  * @param cls client who sent the message
378  * @param rm the message received
379  */
380 static void
381 handle_revoke_message (void *cls,
382                        const struct RevokeMessage *rm)
383 {
384   struct GNUNET_SERVICE_Client *client = cls;
385   struct GNUNET_MQ_Envelope *env;
386   struct RevocationResponseMessage *rrm;
387   int ret;
388
389   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
390               "Received REVOKE message from client\n");
391   if (GNUNET_SYSERR == (ret = publicize_rm (rm)))
392   {
393     GNUNET_break_op (0);
394     GNUNET_SERVICE_client_drop (client);
395     return;
396   }
397   env = GNUNET_MQ_msg (rrm,
398                        GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE);
399   rrm->is_valid = htonl ((GNUNET_OK == ret) ? GNUNET_NO : GNUNET_YES);
400   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
401                   env);
402   GNUNET_SERVICE_client_continue (client);
403 }
404
405
406 /**
407  * Core handler for flooded revocation messages.
408  *
409  * @param cls closure unused
410  * @param rm revocation message
411  */
412 static void
413 handle_p2p_revoke (void *cls,
414                    const struct RevokeMessage *rm)
415 {
416   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417               "Received REVOKE message\n");
418   GNUNET_break_op (GNUNET_SYSERR !=
419                    publicize_rm (rm));
420 }
421
422
423 /**
424  * Callback for set operation results. Called for each element in the
425  * result set.  Each element contains a revocation, which we should
426  * validate and then add to our revocation list (and set).
427  *
428  * @param cls closure
429  * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK
430  * @param current_size current set size
431  * @param status see `enum GNUNET_SET_Status`
432  */
433 static void
434 add_revocation (void *cls,
435                 const struct GNUNET_SET_Element *element,
436                 uint64_t current_size,
437                 enum GNUNET_SET_Status status)
438 {
439   struct PeerEntry *peer_entry = cls;
440   const struct RevokeMessage *rm;
441
442   switch (status)
443   {
444   case GNUNET_SET_STATUS_OK:
445     if (element->size != sizeof(struct RevokeMessage))
446     {
447       GNUNET_break_op (0);
448       return;
449     }
450     if (GNUNET_BLOCK_TYPE_REVOCATION != element->element_type)
451     {
452       GNUNET_STATISTICS_update (stats,
453                                 gettext_noop (
454                                   "# unsupported revocations received via set union"),
455                                 1,
456                                 GNUNET_NO);
457       return;
458     }
459     rm = element->data;
460     (void) handle_p2p_revoke (NULL,
461                               rm);
462     GNUNET_STATISTICS_update (stats,
463                               gettext_noop (
464                                 "# revocation messages received via set union"),
465                               1, GNUNET_NO);
466     break;
467
468   case GNUNET_SET_STATUS_FAILURE:
469     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
470                 _ ("Error computing revocation set union with %s\n"),
471                 GNUNET_i2s (&peer_entry->id));
472     peer_entry->so = NULL;
473     GNUNET_STATISTICS_update (stats,
474                               gettext_noop ("# revocation set unions failed"),
475                               1,
476                               GNUNET_NO);
477     break;
478
479   case GNUNET_SET_STATUS_HALF_DONE:
480     break;
481
482   case GNUNET_SET_STATUS_DONE:
483     peer_entry->so = NULL;
484     GNUNET_STATISTICS_update (stats,
485                               gettext_noop (
486                                 "# revocation set unions completed"),
487                               1,
488                               GNUNET_NO);
489     break;
490
491   default:
492     GNUNET_break (0);
493     break;
494   }
495 }
496
497
498 /**
499  * The timeout for performing the set union has expired,
500  * run the set operation on the revocation certificates.
501  *
502  * @param cls NULL
503  */
504 static void
505 transmit_task_cb (void *cls)
506 {
507   struct PeerEntry *peer_entry = cls;
508
509   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
510               "Starting set exchange with peer `%s'\n",
511               GNUNET_i2s (&peer_entry->id));
512   peer_entry->transmit_task = NULL;
513   GNUNET_assert (NULL == peer_entry->so);
514   peer_entry->so = GNUNET_SET_prepare (&peer_entry->id,
515                                        &revocation_set_union_app_id,
516                                        NULL,
517                                        GNUNET_SET_RESULT_ADDED,
518                                        (struct GNUNET_SET_Option[]) { { 0 } },
519                                        &add_revocation,
520                                        peer_entry);
521   if (GNUNET_OK !=
522       GNUNET_SET_commit (peer_entry->so,
523                          revocation_set))
524   {
525     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
526                 _ ("SET service crashed, terminating revocation service\n"));
527     GNUNET_SCHEDULER_shutdown ();
528     return;
529   }
530 }
531
532
533 /**
534  * Method called whenever a peer connects. Sets up the PeerEntry and
535  * schedules the initial revocation set exchange with this peer.
536  *
537  * @param cls closure
538  * @param peer peer identity this notification is about
539  */
540 static void *
541 handle_core_connect (void *cls,
542                      const struct GNUNET_PeerIdentity *peer,
543                      struct GNUNET_MQ_Handle *mq)
544 {
545   struct PeerEntry *peer_entry;
546   struct GNUNET_HashCode my_hash;
547   struct GNUNET_HashCode peer_hash;
548
549   if (0 == GNUNET_memcmp (peer,
550                           &my_identity))
551   {
552     return NULL;
553   }
554
555   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
556               "Peer `%s' connected to us\n",
557               GNUNET_i2s (peer));
558   GNUNET_STATISTICS_update (stats,
559                             "# peers connected",
560                             1,
561                             GNUNET_NO);
562   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
563                                                   peer);
564   if (NULL != peer_entry)
565   {
566     /* This can happen if "core"'s notification is a tad late
567        and CADET+SET were faster and already produced a
568      #handle_revocation_union_request() for us to deal
569        with.  This should be rare, but isn't impossible. */
570     peer_entry->mq = mq;
571     return peer_entry;
572   }
573   peer_entry = new_peer_entry (peer);
574   peer_entry->mq = mq;
575   GNUNET_CRYPTO_hash (&my_identity,
576                       sizeof(my_identity),
577                       &my_hash);
578   GNUNET_CRYPTO_hash (peer,
579                       sizeof(*peer),
580                       &peer_hash);
581   if (0 < GNUNET_CRYPTO_hash_cmp (&my_hash,
582                                   &peer_hash))
583   {
584     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
585                 "Starting SET operation with peer `%s'\n",
586                 GNUNET_i2s (peer));
587     peer_entry->transmit_task =
588       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
589                                     &transmit_task_cb,
590                                     peer_entry);
591   }
592   return peer_entry;
593 }
594
595
596 /**
597  * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
598  * any pending transmission requests to that peer.
599  *
600  * @param cls closure
601  * @param peer peer identity this notification is about
602  * @param internal_cls our `struct PeerEntry` for this peer
603  */
604 static void
605 handle_core_disconnect (void *cls,
606                         const struct GNUNET_PeerIdentity *peer,
607                         void *internal_cls)
608 {
609   struct PeerEntry *peer_entry = internal_cls;
610
611   if (0 == GNUNET_memcmp (peer,
612                           &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   if (NULL != peer_entry->so)
762   {
763     GNUNET_break_op (0);
764     return;
765   }
766   peer_entry->so = GNUNET_SET_accept (request,
767                                       GNUNET_SET_RESULT_ADDED,
768                                       (struct GNUNET_SET_Option[]) { { 0 } },
769                                       &add_revocation,
770                                       peer_entry);
771   if (GNUNET_OK !=
772       GNUNET_SET_commit (peer_entry->so,
773                          revocation_set))
774   {
775     GNUNET_break (0);
776     GNUNET_SCHEDULER_shutdown ();
777     return;
778   }
779 }
780
781
782 /**
783  * Handle network size estimate clients.
784  *
785  * @param cls closure
786  * @param server the initialized server
787  * @param c configuration to use
788  */
789 static void
790 run (void *cls,
791      const struct GNUNET_CONFIGURATION_Handle *c,
792      struct GNUNET_SERVICE_Handle *service)
793 {
794   struct GNUNET_MQ_MessageHandler core_handlers[] = {
795     GNUNET_MQ_hd_fixed_size (p2p_revoke,
796                              GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
797                              struct RevokeMessage,
798                              NULL),
799     GNUNET_MQ_handler_end ()
800   };
801   char *fn;
802   uint64_t left;
803   struct RevokeMessage *rm;
804   struct GNUNET_HashCode hc;
805
806   GNUNET_CRYPTO_hash ("revocation-set-union-application-id",
807                       strlen ("revocation-set-union-application-id"),
808                       &revocation_set_union_app_id);
809   if (GNUNET_OK !=
810       GNUNET_CONFIGURATION_get_value_filename (c,
811                                                "REVOCATION",
812                                                "DATABASE",
813                                                &fn))
814   {
815     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
816                                "REVOCATION",
817                                "DATABASE");
818     GNUNET_SCHEDULER_shutdown ();
819     return;
820   }
821   cfg = c;
822   revocation_map = GNUNET_CONTAINER_multihashmap_create (16,
823                                                          GNUNET_NO);
824   if (GNUNET_OK !=
825       GNUNET_CONFIGURATION_get_value_number (cfg,
826                                              "REVOCATION",
827                                              "WORKBITS",
828                                              &revocation_work_required))
829   {
830     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
831                                "REVOCATION",
832                                "WORKBITS");
833     GNUNET_SCHEDULER_shutdown ();
834     GNUNET_free (fn);
835     return;
836   }
837   if (revocation_work_required >= sizeof(struct GNUNET_HashCode) * 8)
838   {
839     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
840                                "REVOCATION",
841                                "WORKBITS",
842                                _ ("Value is too large.\n"));
843     GNUNET_SCHEDULER_shutdown ();
844     GNUNET_free (fn);
845     return;
846   }
847   if (GNUNET_OK !=
848       GNUNET_CONFIGURATION_get_value_time (cfg,
849                                            "REVOCATION",
850                                            "EPOCH_DURATION",
851                                            &epoch_duration))
852   {
853     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
854                                "REVOCATION",
855                                "EPOCH_DURATION");
856     GNUNET_SCHEDULER_shutdown ();
857     GNUNET_free (fn);
858     return;
859   }
860
861   revocation_set = GNUNET_SET_create (cfg,
862                                       GNUNET_SET_OPERATION_UNION);
863   revocation_union_listen_handle
864     = GNUNET_SET_listen (cfg,
865                          GNUNET_SET_OPERATION_UNION,
866                          &revocation_set_union_app_id,
867                          &handle_revocation_union_request,
868                          NULL);
869   revocation_db = GNUNET_DISK_file_open (fn,
870                                          GNUNET_DISK_OPEN_READWRITE
871                                          | GNUNET_DISK_OPEN_CREATE,
872                                          GNUNET_DISK_PERM_USER_READ
873                                          | GNUNET_DISK_PERM_USER_WRITE
874                                          | GNUNET_DISK_PERM_GROUP_READ
875                                          | GNUNET_DISK_PERM_OTHER_READ);
876   if (NULL == revocation_db)
877   {
878     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
879                                "REVOCATION",
880                                "DATABASE",
881                                _ ("Could not open revocation database file!"));
882     GNUNET_SCHEDULER_shutdown ();
883     GNUNET_free (fn);
884     return;
885   }
886   if (GNUNET_OK !=
887       GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES))
888     left = 0;
889   while (left > sizeof(struct RevokeMessage))
890   {
891     rm = GNUNET_new (struct RevokeMessage);
892     if (sizeof(struct RevokeMessage) !=
893         GNUNET_DISK_file_read (revocation_db,
894                                rm,
895                                sizeof(struct RevokeMessage)))
896     {
897       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
898                                 "read",
899                                 fn);
900       GNUNET_free (rm);
901       GNUNET_SCHEDULER_shutdown ();
902       GNUNET_free (fn);
903       return;
904     }
905     GNUNET_break (0 == ntohl (rm->reserved));
906     GNUNET_CRYPTO_hash (&rm->proof_of_work.key,
907                         sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
908                         &hc);
909     GNUNET_break (GNUNET_OK ==
910                   GNUNET_CONTAINER_multihashmap_put (revocation_map,
911                                                      &hc,
912                                                      rm,
913                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
914   }
915   GNUNET_free (fn);
916
917   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
918                                  NULL);
919   peers = GNUNET_CONTAINER_multipeermap_create (128,
920                                                 GNUNET_YES);
921   /* Connect to core service and register core handlers */
922   core_api = GNUNET_CORE_connect (cfg,    /* Main configuration */
923                                   NULL,       /* Closure passed to functions */
924                                   &core_init,    /* Call core_init once connected */
925                                   &handle_core_connect,  /* Handle connects */
926                                   &handle_core_disconnect,       /* Handle disconnects */
927                                   core_handlers);        /* Register these handlers */
928   if (NULL == core_api)
929   {
930     GNUNET_SCHEDULER_shutdown ();
931     return;
932   }
933   stats = GNUNET_STATISTICS_create ("revocation",
934                                     cfg);
935 }
936
937
938 /**
939  * Define "main" method using service macro.
940  */
941 GNUNET_SERVICE_MAIN
942   ("revocation",
943   GNUNET_SERVICE_OPTION_NONE,
944   &run,
945   &client_connect_cb,
946   &client_disconnect_cb,
947   NULL,
948   GNUNET_MQ_hd_fixed_size (query_message,
949                            GNUNET_MESSAGE_TYPE_REVOCATION_QUERY,
950                            struct QueryMessage,
951                            NULL),
952   GNUNET_MQ_hd_fixed_size (revoke_message,
953                            GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
954                            struct RevokeMessage,
955                            NULL),
956   GNUNET_MQ_handler_end ());
957
958
959 #if defined(__linux__) && defined(__GLIBC__)
960 #include <malloc.h>
961
962 /**
963  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
964  */
965 void __attribute__ ((constructor))
966 GNUNET_REVOCATION_memory_init ()
967 {
968   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
969   mallopt (M_TOP_PAD, 1 * 1024);
970   malloc_trim (0);
971 }
972
973
974 #endif
975
976
977 /* end of gnunet-service-revocation.c */