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