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