-indent, doxygen
[oweals/gnunet.git] / src / revocation / gnunet-service-revocation.c
1 /*
2   This file is part of GNUnet.
3   (C) 2013 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   GNUNET_SCHEDULER_TaskIdentifier 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, &hc);
204   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205               (GNUNET_NO == res)
206               ? "Received revocation check for valid key `%s' from client\n"
207               : "Received revocation check for revoked key `%s' from client\n",
208               GNUNET_h2s (&hc));
209   qrm.header.size = htons (sizeof (struct QueryResponseMessage));
210   qrm.header.type = htons (GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE);
211   qrm.is_valid = htonl ((GNUNET_YES == res) ? GNUNET_NO : GNUNET_YES);
212   GNUNET_SERVER_notification_context_add (nc,
213                                           client);
214   GNUNET_SERVER_notification_context_unicast (nc,
215                                               client,
216                                               &qrm.header,
217                                               GNUNET_NO);
218   GNUNET_SERVER_receive_done (client, GNUNET_OK);
219 }
220
221
222 /**
223  * Flood the given revocation message to all neighbours.
224  *
225  * @param cls the `struct RevokeMessage` to flood
226  * @param target a neighbour
227  * @param value our `struct PeerEntry` for the neighbour
228  * @return #GNUNET_OK (continue to iterate)
229  */
230 static int
231 do_flood (void *cls,
232           const struct GNUNET_PeerIdentity *target,
233           void *value)
234 {
235   const struct RevokeMessage *rm = cls;
236   struct PeerEntry *pe = value;
237   struct GNUNET_MQ_Envelope *e;
238   struct RevokeMessage *cp;
239
240   e = GNUNET_MQ_msg (cp, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE);
241   *cp = *rm;
242   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flooding revocation to `%s'\n", GNUNET_i2s (target));
243   GNUNET_MQ_send (pe->mq, e);
244   return GNUNET_OK;
245 }
246
247
248 /**
249  * Publicize revocation message.   Stores the message locally in the
250  * database and passes it to all connected neighbours (and adds it to
251  * the set for future connections).
252  *
253  * @param rm message to publicize
254  * @return #GNUNET_OK on success, #GNUNET_NO if we encountered an error,
255  *         #GNUNET_SYSERR if the message was malformed
256  */
257 static int
258 publicize_rm (const struct RevokeMessage *rm)
259 {
260   struct RevokeMessage *cp;
261   struct GNUNET_HashCode hc;
262   struct GNUNET_SET_Element e;
263
264   GNUNET_CRYPTO_hash (&rm->public_key,
265                       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
266                       &hc);
267   if (GNUNET_YES ==
268       GNUNET_CONTAINER_multihashmap_contains (revocation_map,
269                                               &hc))
270   {
271     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
272                 _("Duplicate revocation received from peer. Ignored.\n"));
273     return GNUNET_OK;
274   }
275   if (GNUNET_OK !=
276       verify_revoke_message (rm))
277   {
278     GNUNET_break_op (0);
279     return GNUNET_SYSERR;
280   }
281   /* write to disk */
282   if (sizeof (struct RevokeMessage) !=
283       GNUNET_DISK_file_write (revocation_db,
284                               rm,
285                               sizeof (struct RevokeMessage)))
286   {
287     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
288                          "write");
289     return GNUNET_NO;
290   }
291   if (GNUNET_OK !=
292       GNUNET_DISK_file_sync (revocation_db))
293   {
294     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
295                          "sync");
296     return GNUNET_NO;
297   }
298   /* keep copy in memory */
299   cp = (struct RevokeMessage *) GNUNET_copy_message (&rm->header);
300   GNUNET_break (GNUNET_OK ==
301                 GNUNET_CONTAINER_multihashmap_put (revocation_map,
302                                                    &hc,
303                                                    cp,
304                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
305   /* add to set for future connections */
306   e.size = htons (rm->header.size);
307   e.type = 0;
308   e.data = rm;
309   if (GNUNET_OK !=
310       GNUNET_SET_add_element (revocation_set,
311                               &e,
312                               NULL, NULL))
313   {
314     GNUNET_break (0);
315     return GNUNET_OK;
316   }
317   else
318   {
319     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
320                 "Added revocation info to SET\n");
321   }
322   /* flood to neighbours */
323   GNUNET_CONTAINER_multipeermap_iterate (peers,
324                                          &do_flood,
325                                          cp);
326   return GNUNET_OK;
327 }
328
329
330 /**
331  * Handle REVOKE message from client.
332  *
333  * @param cls unused
334  * @param client who sent the message
335  * @param message the message received
336  */
337 static void
338 handle_revoke_message (void *cls,
339                        struct GNUNET_SERVER_Client *client,
340                        const struct GNUNET_MessageHeader *message)
341 {
342   const struct RevokeMessage *rm;
343   struct RevocationResponseMessage rrm;
344   int ret;
345
346   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347               "Received REVOKE message from client\n");
348   rm = (const struct RevokeMessage *) message;
349   if (GNUNET_SYSERR == (ret = publicize_rm (rm)))
350   {
351     GNUNET_break_op (0);
352     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
353     return;
354   }
355   rrm.header.size = htons (sizeof (struct RevocationResponseMessage));
356   rrm.header.type = htons (GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE);
357   rrm.is_valid = htonl ((GNUNET_OK == ret) ? GNUNET_NO : GNUNET_YES);
358   GNUNET_SERVER_notification_context_add (nc,
359                                           client);
360   GNUNET_SERVER_notification_context_unicast (nc,
361                                               client,
362                                               &rrm.header,
363                                               GNUNET_NO);
364   GNUNET_SERVER_receive_done (client, GNUNET_OK);
365 }
366
367
368 /**
369  * Core handler for flooded revocation messages.
370  *
371  * @param cls closure unused
372  * @param message message
373  * @param peer peer identity this message is from (ignored)
374  */
375 static int
376 handle_p2p_revoke_message (void *cls,
377                            const struct GNUNET_PeerIdentity *peer,
378                            const struct GNUNET_MessageHeader *message)
379 {
380   const struct RevokeMessage *rm;
381
382   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
383               "Received REVOKE message from peer\n");
384   rm = (const struct RevokeMessage *) message;
385   GNUNET_break_op (GNUNET_SYSERR != publicize_rm (rm));
386   return GNUNET_OK;
387 }
388
389
390
391 /**
392  * Callback for set operation results. Called for each element in the
393  * result set.  Each element contains a revocation, which we should
394  * validate and then add to our revocation list (and set).
395  *
396  * @param cls closure
397  * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK
398  * @param status see `enum GNUNET_SET_Status`
399  */
400 static void
401 add_revocation (void *cls,
402                 const struct GNUNET_SET_Element *element,
403                 enum GNUNET_SET_Status status)
404 {
405   struct PeerEntry *peer_entry = cls;
406   const struct RevokeMessage *rm;
407
408   switch (status)
409   {
410   case GNUNET_SET_STATUS_OK:
411     if (element->size != sizeof (struct RevokeMessage))
412     {
413       GNUNET_break_op (0);
414       return;
415     }
416     if (0 != element->type)
417     {
418       GNUNET_STATISTICS_update (stats,
419                                 "# unsupported revocations received via set union",
420                                 1, GNUNET_NO);
421       return;
422     }
423     rm = element->data;
424     (void) handle_p2p_revoke_message (NULL,
425                                       &peer_entry->id,
426                                       &rm->header);
427     GNUNET_STATISTICS_update (stats,
428                               "# revocation messages received via set union",
429                               1, GNUNET_NO);
430     break;
431   case GNUNET_SET_STATUS_TIMEOUT:
432   case GNUNET_SET_STATUS_FAILURE:
433     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
434                 _("Error computing revocation set union with %s\n"),
435                 GNUNET_i2s (&peer_entry->id));
436     peer_entry->so = NULL;
437     GNUNET_STATISTICS_update (stats, "# revocation set unions failed",
438                               1, GNUNET_NO);
439     break;
440   case GNUNET_SET_STATUS_HALF_DONE:
441     break;
442   case GNUNET_SET_STATUS_DONE:
443     peer_entry->so = NULL;
444     GNUNET_STATISTICS_update (stats, "# revocation set unions completed",
445                               1, GNUNET_NO);
446     break;
447   default:
448     GNUNET_break (0);
449     break;
450  }
451 }
452
453
454 /**
455  * The timeout for performing the set union has expired,
456  * run the set operation on the revocation certificates.
457  *
458  * @param cls NULL
459  * @param tc scheduler context (unused)
460  */
461 static void
462 transmit_task_cb (void *cls,
463                   const struct GNUNET_SCHEDULER_TaskContext *tc)
464 {
465   struct PeerEntry *peer_entry = cls;
466   uint16_t salt;
467
468   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
469               "Starting set exchange with peer `%s'\n",
470               GNUNET_i2s (&peer_entry->id));
471
472   salt = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
473                                               UINT16_MAX);
474   peer_entry->transmit_task = GNUNET_SCHEDULER_NO_TASK;
475   peer_entry->so = GNUNET_SET_prepare (&peer_entry->id,
476                                        &revocation_set_union_app_id,
477                                        NULL, salt,
478                                        GNUNET_SET_RESULT_ADDED,
479                                        &add_revocation,
480                                        peer_entry);
481   if (GNUNET_OK !=
482       GNUNET_SET_commit (peer_entry->so,
483                          revocation_set))
484   {
485     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
486                 _("SET service crashed, terminating revocation service\n"));
487     GNUNET_SCHEDULER_shutdown ();
488     return;
489   }
490 }
491
492
493 /**
494  * Method called whenever a peer connects. Sets up the PeerEntry and
495  * schedules the initial revocation set exchange with this peer.
496  *
497  * @param cls closure
498  * @param peer peer identity this notification is about
499  */
500 static void
501 handle_core_connect (void *cls,
502                      const struct GNUNET_PeerIdentity *peer)
503 {
504   struct PeerEntry *peer_entry;
505   struct GNUNET_HashCode my_hash;
506   struct GNUNET_HashCode peer_hash;
507
508   if (0 == memcmp(peer, &my_identity, sizeof (my_identity)))
509       return;
510
511   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
512               "Peer `%s' connected to us\n",
513               GNUNET_i2s (peer));
514   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
515                                                   peer);
516   if (NULL == peer_entry)
517   {
518     peer_entry = GNUNET_new (struct PeerEntry);
519     peer_entry->id = *peer;
520     GNUNET_assert (GNUNET_OK ==
521                    GNUNET_CONTAINER_multipeermap_put (peers, peer,
522                                                       peer_entry,
523                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
524   }
525   else
526   {
527     GNUNET_assert (NULL == peer_entry->mq);
528   }
529   peer_entry->mq = GNUNET_CORE_mq_create (core_api, peer);
530   GNUNET_CRYPTO_hash (&my_identity, sizeof (my_identity), &my_hash);
531   GNUNET_CRYPTO_hash (peer, sizeof (*peer), &peer_hash);
532   if (0 < GNUNET_CRYPTO_hash_cmp (&my_hash,
533                                   &peer_hash))
534   {
535     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
536                 "Starting SET operation with peer `%s'\n",
537                 GNUNET_i2s (peer));
538     peer_entry->transmit_task =
539       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
540                                     &transmit_task_cb,
541                                     peer_entry);
542   }
543   GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
544 }
545
546
547 /**
548  * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
549  * any pending transmission requests to that peer.
550  *
551  * @param cls closure
552  * @param peer peer identity this notification is about
553  */
554 static void
555 handle_core_disconnect (void *cls,
556                         const struct GNUNET_PeerIdentity *peer)
557 {
558   struct PeerEntry *pos;
559
560   if (0 == memcmp(peer, &my_identity, sizeof (my_identity)))
561       return;
562
563   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564               "Peer `%s' disconnected from us\n",
565               GNUNET_i2s (peer));
566   pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
567   if (NULL == pos)
568   {
569     GNUNET_break (0);
570     return;
571   }
572   GNUNET_assert (GNUNET_YES ==
573                  GNUNET_CONTAINER_multipeermap_remove (peers, peer,
574                                                        pos));
575   GNUNET_MQ_destroy (pos->mq);
576   if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK)
577   {
578     GNUNET_SCHEDULER_cancel (pos->transmit_task);
579     pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
580   }
581   if (NULL != pos->so)
582   {
583     GNUNET_SET_operation_cancel (pos->so);
584     pos->so = NULL;
585   }
586   GNUNET_free (pos);
587   GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
588 }
589
590
591 /**
592  * Free all values in a hash map.
593  *
594  * @param cls NULL
595  * @param key the key
596  * @param value value to free
597  * @return #GNUNET_OK (continue to iterate)
598  */
599 static int
600 free_entry (void *cls,
601             const struct GNUNET_HashCode *key,
602             void *value)
603 {
604   GNUNET_free (value);
605   return GNUNET_OK;
606 }
607
608
609 /**
610  * Task run during shutdown.
611  *
612  * @param cls unused
613  * @param tc unused
614  */
615 static void
616 shutdown_task (void *cls,
617                const struct GNUNET_SCHEDULER_TaskContext *tc)
618 {
619   if (NULL != revocation_set)
620   {
621     GNUNET_SET_destroy (revocation_set);
622     revocation_set = NULL;
623   }
624   if (NULL != revocation_union_listen_handle)
625   {
626     GNUNET_SET_listen_cancel (revocation_union_listen_handle);
627     revocation_union_listen_handle = NULL;
628   }
629   if (NULL != core_api)
630   {
631     GNUNET_CORE_disconnect (core_api);
632     core_api = NULL;
633   }
634   if (NULL != stats)
635   {
636     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
637     stats = NULL;
638   }
639   if (NULL != peers)
640   {
641     GNUNET_CONTAINER_multipeermap_destroy (peers);
642     peers = NULL;
643   }
644   if (NULL != nc)
645   {
646     GNUNET_SERVER_notification_context_destroy (nc);
647     nc = NULL;
648   }
649   if (NULL != revocation_db)
650   {
651     GNUNET_DISK_file_close (revocation_db);
652     revocation_db = NULL;
653   }
654   GNUNET_CONTAINER_multihashmap_iterate (revocation_map,
655                                          &free_entry,
656                                          NULL);
657   GNUNET_CONTAINER_multihashmap_destroy (revocation_map);
658 }
659
660
661 /**
662  * Called on core init/fail.
663  *
664  * @param cls service closure
665  * @param identity the public identity of this peer
666  */
667 static void
668 core_init (void *cls,
669            const struct GNUNET_PeerIdentity *identity)
670 {
671   if (NULL == identity)
672   {
673     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
674                 "Connection to core FAILED!\n");
675     GNUNET_SCHEDULER_shutdown ();
676     return;
677   }
678   my_identity = *identity;
679 }
680
681
682 /**
683  * Called when another peer wants to do a set operation with the
684  * local peer. If a listen error occurs, the 'request' is NULL.
685  *
686  * @param cls closure
687  * @param other_peer the other peer
688  * @param context_msg message with application specific information from
689  *        the other peer
690  * @param request request from the other peer (never NULL), use GNUNET_SET_accept()
691  *        to accept it, otherwise the request will be refused
692  *        Note that we can't just return value from the listen callback,
693  *        as it is also necessary to specify the set we want to do the
694  *        operation with, whith sometimes can be derived from the context
695  *        message. It's necessary to specify the timeout.
696  */
697 static void
698 handle_revocation_union_request (void *cls,
699                                  const struct GNUNET_PeerIdentity *other_peer,
700                                  const struct GNUNET_MessageHeader *context_msg,
701                                  struct GNUNET_SET_Request *request)
702 {
703   struct PeerEntry *peer_entry;
704
705   if (NULL == request)
706   {
707     GNUNET_break (0);
708     return;
709   }
710   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
711               "Received set exchange request from peer `%s'\n",
712               GNUNET_i2s (other_peer));
713   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
714                                                   other_peer);
715   if (NULL == peer_entry)
716   {
717     peer_entry = GNUNET_new (struct PeerEntry);
718     peer_entry->id = *other_peer;
719     GNUNET_assert (GNUNET_OK ==
720                    GNUNET_CONTAINER_multipeermap_put (peers, other_peer,
721                                                       peer_entry,
722                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
723   }
724   peer_entry->so = GNUNET_SET_accept (request,
725                                       GNUNET_SET_RESULT_ADDED,
726                                       &add_revocation,
727                                       peer_entry);
728   if (GNUNET_OK !=
729       GNUNET_SET_commit (peer_entry->so,
730                          revocation_set))
731   {
732     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
733                 _("SET service crashed, terminating revocation service\n"));
734     GNUNET_SCHEDULER_shutdown ();
735     return;
736   }
737 }
738
739
740 /**
741  * Handle network size estimate clients.
742  *
743  * @param cls closure
744  * @param server the initialized server
745  * @param c configuration to use
746  */
747 static void
748 run (void *cls,
749      struct GNUNET_SERVER_Handle *server,
750      const struct GNUNET_CONFIGURATION_Handle *c)
751 {
752   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
753     {&handle_query_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_QUERY,
754      sizeof (struct QueryMessage)},
755     {&handle_revoke_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
756      sizeof (struct RevokeMessage)},
757     {NULL, NULL, 0, 0}
758   };
759   static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
760     {&handle_p2p_revoke_message, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
761      sizeof (struct RevokeMessage)},
762     {NULL, 0, 0}
763   };
764   char *fn;
765   uint64_t left;
766   struct RevokeMessage *rm;
767   struct GNUNET_HashCode hc;
768
769   if (GNUNET_OK !=
770       GNUNET_CONFIGURATION_get_value_filename (c,
771                                                "REVOCATION",
772                                                "DATABASE",
773                                                &fn))
774   {
775     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
776                                "REVOCATION",
777                                "DATABASE");
778     GNUNET_SCHEDULER_shutdown ();
779     return;
780   }
781   cfg = c;
782   srv = server;
783   revocation_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
784   nc = GNUNET_SERVER_notification_context_create (server, 1);
785   if (GNUNET_OK !=
786       GNUNET_CONFIGURATION_get_value_number (cfg, "REVOCATION", "WORKBITS",
787                                              &revocation_work_required))
788   {
789     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
790                                "REVOCATION",
791                                "WORKBITS");
792     GNUNET_SCHEDULER_shutdown ();
793     GNUNET_free (fn);
794     return;
795   }
796   if (revocation_work_required >= sizeof (struct GNUNET_HashCode) * 8)
797   {
798     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
799                                "REVOCATION",
800                                "WORKBITS",
801                                _("Value is too large.\n"));
802     GNUNET_SCHEDULER_shutdown ();
803     GNUNET_free (fn);
804     return;
805   }
806   revocation_set = GNUNET_SET_create (cfg,
807                                       GNUNET_SET_OPERATION_UNION);
808   revocation_union_listen_handle = GNUNET_SET_listen (cfg,
809                                                       GNUNET_SET_OPERATION_UNION,
810                                                       &revocation_set_union_app_id,
811                                                       &handle_revocation_union_request,
812                                                       NULL);
813   revocation_db = GNUNET_DISK_file_open (fn,
814                                          GNUNET_DISK_OPEN_READWRITE |
815                                          GNUNET_DISK_OPEN_CREATE,
816                                          GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE |
817                                          GNUNET_DISK_PERM_GROUP_READ |
818                                          GNUNET_DISK_PERM_OTHER_READ);
819   if (NULL == revocation_db)
820   {
821     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
822                                "REVOCATION",
823                                "DATABASE",
824                                _("Could not open revocation database file!"));
825     GNUNET_SCHEDULER_shutdown ();
826     GNUNET_free (fn);
827     return;
828   }
829   if (GNUNET_OK !=
830       GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES))
831     left = 0;
832   while (left > sizeof (struct RevokeMessage))
833   {
834     rm = GNUNET_new (struct RevokeMessage);
835     if (sizeof (struct RevokeMessage) !=
836         GNUNET_DISK_file_read (revocation_db,
837                                rm,
838                                sizeof (struct RevokeMessage)))
839     {
840       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
841                                 "read",
842                                 fn);
843       GNUNET_free (rm);
844       GNUNET_SCHEDULER_shutdown ();
845       GNUNET_free (fn);
846       return;
847     }
848     GNUNET_break (0 == ntohl (rm->reserved));
849     GNUNET_CRYPTO_hash (&rm->public_key,
850                         sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
851                         &hc);
852     GNUNET_break (GNUNET_OK ==
853                   GNUNET_CONTAINER_multihashmap_put (revocation_map,
854                                                      &hc,
855                                                      rm,
856                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
857   }
858   GNUNET_free (fn);
859
860   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
861                                 NULL);
862   peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
863   GNUNET_SERVER_add_handlers (srv, handlers);
864    /* Connect to core service and register core handlers */
865   core_api = GNUNET_CORE_connect (cfg,   /* Main configuration */
866                                  NULL,       /* Closure passed to functions */
867                                  &core_init,    /* Call core_init once connected */
868                                  &handle_core_connect,  /* Handle connects */
869                                  &handle_core_disconnect,       /* Handle disconnects */
870                                  NULL,  /* Don't want notified about all incoming messages */
871                                  GNUNET_NO,     /* For header only inbound notification */
872                                  NULL,  /* Don't want notified about all outbound messages */
873                                  GNUNET_NO,     /* For header only outbound notification */
874                                  core_handlers);        /* Register these handlers */
875   if (NULL == core_api)
876   {
877     GNUNET_SCHEDULER_shutdown ();
878     return;
879   }
880   stats = GNUNET_STATISTICS_create ("revocation", cfg);
881 }
882
883
884 /**
885  * The main function for the network size estimation service.
886  *
887  * @param argc number of arguments from the command line
888  * @param argv command line arguments
889  * @return 0 ok, 1 on error
890  */
891 int
892 main (int argc,
893       char *const *argv)
894 {
895   GNUNET_CRYPTO_hash ("revocation-set-union-application-id",
896                       strlen ("revocation-set-union-application-id"),
897                       &revocation_set_union_app_id);
898   return (GNUNET_OK ==
899           GNUNET_SERVICE_run (argc, argv, "revocation", GNUNET_SERVICE_OPTION_NONE,
900                               &run, NULL)) ? 0 : 1;
901 }
902
903
904 #ifdef LINUX
905 #include <malloc.h>
906
907 /**
908  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
909  */
910 void __attribute__ ((constructor))
911 GNUNET_ARM_memory_init ()
912 {
913   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
914   mallopt (M_TOP_PAD, 1 * 1024);
915   malloc_trim (0);
916 }
917 #endif
918
919
920
921 /* end of gnunet-service-revocation.c */