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