rps: store valid peer ids in file
[oweals/gnunet.git] / src / revocation / gnunet-service-revocation.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2013, 2014 GNUnet e.V.
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  */
525 static void
526 transmit_task_cb (void *cls)
527 {
528   struct PeerEntry *peer_entry = cls;
529
530   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
531               "Starting set exchange with peer `%s'\n",
532               GNUNET_i2s (&peer_entry->id));
533   peer_entry->transmit_task = NULL;
534   peer_entry->so = GNUNET_SET_prepare (&peer_entry->id,
535                                        &revocation_set_union_app_id,
536                                        NULL,
537                                        GNUNET_SET_RESULT_ADDED,
538                                        &add_revocation,
539                                        peer_entry);
540   if (GNUNET_OK !=
541       GNUNET_SET_commit (peer_entry->so,
542                          revocation_set))
543   {
544     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
545                 _("SET service crashed, terminating revocation service\n"));
546     GNUNET_SCHEDULER_shutdown ();
547     return;
548   }
549 }
550
551
552 /**
553  * Method called whenever a peer connects. Sets up the PeerEntry and
554  * schedules the initial revocation set exchange with this peer.
555  *
556  * @param cls closure
557  * @param peer peer identity this notification is about
558  */
559 static void
560 handle_core_connect (void *cls,
561                      const struct GNUNET_PeerIdentity *peer)
562 {
563   struct PeerEntry *peer_entry;
564   struct GNUNET_HashCode my_hash;
565   struct GNUNET_HashCode peer_hash;
566
567   if (0 == memcmp (peer,
568                    &my_identity,
569                    sizeof (my_identity)))
570   {
571     return;
572   }
573
574   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575               "Peer `%s' connected to us\n",
576               GNUNET_i2s (peer));
577   GNUNET_STATISTICS_update (stats,
578                             "# peers connected",
579                             1,
580                             GNUNET_NO);
581   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
582                                                   peer);
583   if (NULL != peer_entry)
584   {
585     /* This can happen if "core"'s notification is a tad late
586        and CADET+SET were faster and already produced a
587        #handle_revocation_union_request() for us to deal
588        with.  This should be rare, but isn't impossible. */
589     return;
590   }
591   peer_entry = new_peer_entry(peer);
592   GNUNET_CRYPTO_hash (&my_identity,
593                       sizeof (my_identity),
594                       &my_hash);
595   GNUNET_CRYPTO_hash (peer,
596                       sizeof (*peer),
597                       &peer_hash);
598   if (0 < GNUNET_CRYPTO_hash_cmp (&my_hash,
599                                   &peer_hash))
600   {
601     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602                 "Starting SET operation with peer `%s'\n",
603                 GNUNET_i2s (peer));
604     peer_entry->transmit_task =
605       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
606                                     &transmit_task_cb,
607                                     peer_entry);
608   }
609 }
610
611
612 /**
613  * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
614  * any pending transmission requests to that peer.
615  *
616  * @param cls closure
617  * @param peer peer identity this notification is about
618  */
619 static void
620 handle_core_disconnect (void *cls,
621                         const struct GNUNET_PeerIdentity *peer)
622 {
623   if (0 == memcmp (peer,
624                    &my_identity,
625                    sizeof (my_identity)))
626   {
627     return;
628   }
629
630   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
631               "Peer `%s' disconnected from us\n",
632               GNUNET_i2s (peer));
633   delete_peer_entry(peer);
634   GNUNET_STATISTICS_update (stats,
635                             "# peers connected",
636                             -1,
637                             GNUNET_NO);
638 }
639
640
641 /**
642  * Free all values in a hash map.
643  *
644  * @param cls NULL
645  * @param key the key
646  * @param value value to free
647  * @return #GNUNET_OK (continue to iterate)
648  */
649 static int
650 free_entry (void *cls,
651             const struct GNUNET_HashCode *key,
652             void *value)
653 {
654   GNUNET_free (value);
655   return GNUNET_OK;
656 }
657
658
659 /**
660  * Task run during shutdown.
661  *
662  * @param cls unused
663  */
664 static void
665 shutdown_task (void *cls)
666 {
667   if (NULL != revocation_set)
668   {
669     GNUNET_SET_destroy (revocation_set);
670     revocation_set = NULL;
671   }
672   if (NULL != revocation_union_listen_handle)
673   {
674     GNUNET_SET_listen_cancel (revocation_union_listen_handle);
675     revocation_union_listen_handle = NULL;
676   }
677   if (NULL != core_api)
678   {
679     GNUNET_CORE_disconnect (core_api);
680     core_api = NULL;
681   }
682   if (NULL != stats)
683   {
684     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
685     stats = NULL;
686   }
687   if (NULL != peers)
688   {
689     GNUNET_CONTAINER_multipeermap_destroy (peers);
690     peers = NULL;
691   }
692   if (NULL != nc)
693   {
694     GNUNET_SERVER_notification_context_destroy (nc);
695     nc = NULL;
696   }
697   if (NULL != revocation_db)
698   {
699     GNUNET_DISK_file_close (revocation_db);
700     revocation_db = NULL;
701   }
702   GNUNET_CONTAINER_multihashmap_iterate (revocation_map,
703                                          &free_entry,
704                                          NULL);
705   GNUNET_CONTAINER_multihashmap_destroy (revocation_map);
706 }
707
708
709 /**
710  * Called on core init/fail.
711  *
712  * @param cls service closure
713  * @param identity the public identity of this peer
714  */
715 static void
716 core_init (void *cls,
717            const struct GNUNET_PeerIdentity *identity)
718 {
719   if (NULL == identity)
720   {
721     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
722                 "Connection to core FAILED!\n");
723     GNUNET_SCHEDULER_shutdown ();
724     return;
725   }
726   my_identity = *identity;
727 }
728
729
730 /**
731  * Called when another peer wants to do a set operation with the
732  * local peer. If a listen error occurs, the 'request' is NULL.
733  *
734  * @param cls closure
735  * @param other_peer the other peer
736  * @param context_msg message with application specific information from
737  *        the other peer
738  * @param request request from the other peer (never NULL), use GNUNET_SET_accept()
739  *        to accept it, otherwise the request will be refused
740  *        Note that we can't just return value from the listen callback,
741  *        as it is also necessary to specify the set we want to do the
742  *        operation with, whith sometimes can be derived from the context
743  *        message. It's necessary to specify the timeout.
744  */
745 static void
746 handle_revocation_union_request (void *cls,
747                                  const struct GNUNET_PeerIdentity *other_peer,
748                                  const struct GNUNET_MessageHeader *context_msg,
749                                  struct GNUNET_SET_Request *request)
750 {
751   struct PeerEntry *peer_entry;
752
753   if (NULL == request)
754   {
755     GNUNET_break (0);
756     return;
757   }
758   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
759               "Received set exchange request from peer `%s'\n",
760               GNUNET_i2s (other_peer));
761   peer_entry = GNUNET_CONTAINER_multipeermap_get (peers,
762                                                   other_peer);
763   if (NULL == peer_entry)
764   {
765     peer_entry = new_peer_entry(other_peer);
766   }
767   peer_entry->so = GNUNET_SET_accept (request,
768                                       GNUNET_SET_RESULT_ADDED,
769                                       &add_revocation,
770                                       peer_entry);
771   if (GNUNET_OK !=
772       GNUNET_SET_commit (peer_entry->so,
773                          revocation_set))
774   {
775     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
776                 _("SET service crashed, terminating revocation service\n"));
777     GNUNET_SCHEDULER_shutdown ();
778     return;
779   }
780 }
781
782
783 /**
784  * Handle network size estimate clients.
785  *
786  * @param cls closure
787  * @param server the initialized server
788  * @param c configuration to use
789  */
790 static void
791 run (void *cls,
792      struct GNUNET_SERVER_Handle *server,
793      const struct GNUNET_CONFIGURATION_Handle *c)
794 {
795   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
796     {&handle_query_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_QUERY,
797      sizeof (struct QueryMessage)},
798     {&handle_revoke_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
799      sizeof (struct RevokeMessage)},
800     {NULL, NULL, 0, 0}
801   };
802   static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
803     {&handle_p2p_revoke_message, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
804      sizeof (struct RevokeMessage)},
805     {NULL, 0, 0}
806   };
807   char *fn;
808   uint64_t left;
809   struct RevokeMessage *rm;
810   struct GNUNET_HashCode hc;
811
812   if (GNUNET_OK !=
813       GNUNET_CONFIGURATION_get_value_filename (c,
814                                                "REVOCATION",
815                                                "DATABASE",
816                                                &fn))
817   {
818     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
819                                "REVOCATION",
820                                "DATABASE");
821     GNUNET_SCHEDULER_shutdown ();
822     return;
823   }
824   cfg = c;
825   srv = server;
826   revocation_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
827   nc = GNUNET_SERVER_notification_context_create (server, 1);
828   if (GNUNET_OK !=
829       GNUNET_CONFIGURATION_get_value_number (cfg, "REVOCATION", "WORKBITS",
830                                              &revocation_work_required))
831   {
832     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
833                                "REVOCATION",
834                                "WORKBITS");
835     GNUNET_SCHEDULER_shutdown ();
836     GNUNET_free (fn);
837     return;
838   }
839   if (revocation_work_required >= sizeof (struct GNUNET_HashCode) * 8)
840   {
841     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
842                                "REVOCATION",
843                                "WORKBITS",
844                                _("Value is too large.\n"));
845     GNUNET_SCHEDULER_shutdown ();
846     GNUNET_free (fn);
847     return;
848   }
849   revocation_set = GNUNET_SET_create (cfg,
850                                       GNUNET_SET_OPERATION_UNION);
851   revocation_union_listen_handle
852     = GNUNET_SET_listen (cfg,
853                          GNUNET_SET_OPERATION_UNION,
854                          &revocation_set_union_app_id,
855                          &handle_revocation_union_request,
856                          NULL);
857   revocation_db = GNUNET_DISK_file_open (fn,
858                                          GNUNET_DISK_OPEN_READWRITE |
859                                          GNUNET_DISK_OPEN_CREATE,
860                                          GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE |
861                                          GNUNET_DISK_PERM_GROUP_READ |
862                                          GNUNET_DISK_PERM_OTHER_READ);
863   if (NULL == revocation_db)
864   {
865     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
866                                "REVOCATION",
867                                "DATABASE",
868                                _("Could not open revocation database file!"));
869     GNUNET_SCHEDULER_shutdown ();
870     GNUNET_free (fn);
871     return;
872   }
873   if (GNUNET_OK !=
874       GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES))
875     left = 0;
876   while (left > sizeof (struct RevokeMessage))
877   {
878     rm = GNUNET_new (struct RevokeMessage);
879     if (sizeof (struct RevokeMessage) !=
880         GNUNET_DISK_file_read (revocation_db,
881                                rm,
882                                sizeof (struct RevokeMessage)))
883     {
884       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
885                                 "read",
886                                 fn);
887       GNUNET_free (rm);
888       GNUNET_SCHEDULER_shutdown ();
889       GNUNET_free (fn);
890       return;
891     }
892     GNUNET_break (0 == ntohl (rm->reserved));
893     GNUNET_CRYPTO_hash (&rm->public_key,
894                         sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
895                         &hc);
896     GNUNET_break (GNUNET_OK ==
897                   GNUNET_CONTAINER_multihashmap_put (revocation_map,
898                                                      &hc,
899                                                      rm,
900                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
901   }
902   GNUNET_free (fn);
903
904   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
905                                  NULL);
906   peers = GNUNET_CONTAINER_multipeermap_create (128,
907                                                 GNUNET_YES);
908   GNUNET_SERVER_add_handlers (srv, handlers);
909    /* Connect to core service and register core handlers */
910   core_api = GNUNET_CORE_connect (cfg,   /* Main configuration */
911                                  NULL,       /* Closure passed to functions */
912                                  &core_init,    /* Call core_init once connected */
913                                  &handle_core_connect,  /* Handle connects */
914                                  &handle_core_disconnect,       /* Handle disconnects */
915                                  NULL,  /* Don't want notified about all incoming messages */
916                                  GNUNET_NO,     /* For header only inbound notification */
917                                  NULL,  /* Don't want notified about all outbound messages */
918                                  GNUNET_NO,     /* For header only outbound notification */
919                                  core_handlers);        /* Register these handlers */
920   if (NULL == core_api)
921   {
922     GNUNET_SCHEDULER_shutdown ();
923     return;
924   }
925   stats = GNUNET_STATISTICS_create ("revocation", cfg);
926 }
927
928
929 /**
930  * The main function for the network size estimation service.
931  *
932  * @param argc number of arguments from the command line
933  * @param argv command line arguments
934  * @return 0 ok, 1 on error
935  */
936 int
937 main (int argc,
938       char *const *argv)
939 {
940   GNUNET_CRYPTO_hash ("revocation-set-union-application-id",
941                       strlen ("revocation-set-union-application-id"),
942                       &revocation_set_union_app_id);
943   return (GNUNET_OK ==
944           GNUNET_SERVICE_run (argc,
945                               argv,
946                               "revocation",
947                               GNUNET_SERVICE_OPTION_NONE,
948                               &run, NULL)) ? 0 : 1;
949 }
950
951
952 #if defined(LINUX) && defined(__GLIBC__)
953 #include <malloc.h>
954
955
956 /**
957  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
958  */
959 void __attribute__ ((constructor))
960 GNUNET_ARM_memory_init ()
961 {
962   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
963   mallopt (M_TOP_PAD, 1 * 1024);
964   malloc_trim (0);
965 }
966 #endif
967
968
969 /* end of gnunet-service-revocation.c */