uncrustify as demanded.
[oweals/gnunet.git] / src / zonemaster / gnunet-service-zonemaster-monitor.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2013, 2014, 2017, 2018 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 zonemaster/gnunet-service-zonemaster-monitor.c
23  * @brief monitor namestore changes and publish them immediately to GNUnet name system
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dht_service.h"
29 #include "gnunet_namestore_service.h"
30 #include "gnunet_statistics_service.h"
31
32
33 #define LOG_STRERROR_FILE(kind, syscall, filename) GNUNET_log_from_strerror_file(kind, "util", syscall, filename)
34
35
36 /**
37  * How often should we (re)publish each record before
38  * it expires?
39  */
40 #define PUBLISH_OPS_PER_EXPIRATION 4
41
42 /**
43  * How many pending DHT operations do we allow at most?
44  */
45 #define DHT_QUEUE_LIMIT 2000
46
47 /**
48  * How many events may the namestore give us before it has to wait
49  * for us to keep up?
50  */
51 #define NAMESTORE_QUEUE_LIMIT 5
52
53 /**
54  * What replication level do we use for DHT PUT operations?
55  */
56 #define DHT_GNS_REPLICATION_LEVEL 5
57
58
59 /**
60  * Handle for DHT PUT activity triggered from the namestore monitor.
61  */
62 struct DhtPutActivity {
63   /**
64    * Kept in a DLL.
65    */
66   struct DhtPutActivity *next;
67
68   /**
69    * Kept in a DLL.
70    */
71   struct DhtPutActivity *prev;
72
73   /**
74    * Handle for the DHT PUT operation.
75    */
76   struct GNUNET_DHT_PutHandle *ph;
77
78   /**
79    * When was this PUT initiated?
80    */
81   struct GNUNET_TIME_Absolute start_date;
82 };
83
84
85 /**
86  * Handle to the statistics service
87  */
88 static struct GNUNET_STATISTICS_Handle *statistics;
89
90 /**
91  * Our handle to the DHT
92  */
93 static struct GNUNET_DHT_Handle *dht_handle;
94
95 /**
96  * Our handle to the namestore service
97  */
98 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
99
100 /**
101  * Handle to monitor namestore changes to instant propagation.
102  */
103 static struct GNUNET_NAMESTORE_ZoneMonitor *zmon;
104
105 /**
106  * Head of monitor activities; kept in a DLL.
107  */
108 static struct DhtPutActivity *ma_head;
109
110 /**
111  * Tail of monitor activities; kept in a DLL.
112  */
113 static struct DhtPutActivity *ma_tail;
114
115 /**
116  * Number of entries in the DHT queue #ma_head.
117  */
118 static unsigned int ma_queue_length;
119
120 /**
121  * Optimize block insertion by caching map of private keys to
122  * public keys in memory?
123  */
124 static int cache_keys;
125
126
127 /**
128  * Task run during shutdown.
129  *
130  * @param cls unused
131  * @param tc unused
132  */
133 static void
134 shutdown_task(void *cls)
135 {
136   struct DhtPutActivity *ma;
137
138   (void)cls;
139   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
140              "Shutting down!\n");
141   while (NULL != (ma = ma_head))
142     {
143       GNUNET_DHT_put_cancel(ma->ph);
144       ma_queue_length--;
145       GNUNET_CONTAINER_DLL_remove(ma_head,
146                                   ma_tail,
147                                   ma);
148       GNUNET_free(ma);
149     }
150   if (NULL != statistics)
151     {
152       GNUNET_STATISTICS_destroy(statistics,
153                                 GNUNET_NO);
154       statistics = NULL;
155     }
156   if (NULL != zmon)
157     {
158       GNUNET_NAMESTORE_zone_monitor_stop(zmon);
159       zmon = NULL;
160     }
161   if (NULL != namestore_handle)
162     {
163       GNUNET_NAMESTORE_disconnect(namestore_handle);
164       namestore_handle = NULL;
165     }
166   if (NULL != dht_handle)
167     {
168       GNUNET_DHT_disconnect(dht_handle);
169       dht_handle = NULL;
170     }
171 }
172
173
174 /**
175  * Continuation called from DHT once the PUT operation triggered
176  * by a monitor is done.
177  *
178  * @param cls a `struct DhtPutActivity`
179  */
180 static void
181 dht_put_monitor_continuation(void *cls)
182 {
183   struct DhtPutActivity *ma = cls;
184
185   GNUNET_NAMESTORE_zone_monitor_next(zmon,
186                                      1);
187   ma_queue_length--;
188   GNUNET_CONTAINER_DLL_remove(ma_head,
189                               ma_tail,
190                               ma);
191   GNUNET_free(ma);
192 }
193
194
195 /**
196  * Convert namestore records from the internal format to that
197  * suitable for publication (removes private records, converts
198  * to absolute expiration time).
199  *
200  * @param rd input records
201  * @param rd_count size of the @a rd and @a rd_public arrays
202  * @param rd_public where to write the converted records
203  * @return number of records written to @a rd_public
204  */
205 static unsigned int
206 convert_records_for_export(const struct GNUNET_GNSRECORD_Data *rd,
207                            unsigned int rd_count,
208                            struct GNUNET_GNSRECORD_Data *rd_public)
209 {
210   struct GNUNET_TIME_Absolute now;
211   unsigned int rd_public_count;
212
213   rd_public_count = 0;
214   now = GNUNET_TIME_absolute_get();
215   for (unsigned int i = 0; i < rd_count; i++)
216     {
217       if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE))
218         continue;
219       if ((0 == (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) &&
220           (rd[i].expiration_time < now.abs_value_us))
221         continue; /* record already expired, skip it */
222       rd_public[rd_public_count++] = rd[i];
223     }
224   return rd_public_count;
225 }
226
227
228 /**
229  * Store GNS records in the DHT.
230  *
231  * @param key key of the zone
232  * @param label label to store under
233  * @param rd_public public record data
234  * @param rd_public_count number of records in @a rd_public
235  * @param ma handle for the PUT operation
236  * @return DHT PUT handle, NULL on error
237  */
238 static struct GNUNET_DHT_PutHandle *
239 perform_dht_put(const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
240                 const char *label,
241                 const struct GNUNET_GNSRECORD_Data *rd_public,
242                 unsigned int rd_public_count,
243                 struct DhtPutActivity *ma)
244 {
245   struct GNUNET_GNSRECORD_Block *block;
246   struct GNUNET_HashCode query;
247   struct GNUNET_TIME_Absolute expire;
248   size_t block_size;
249   struct GNUNET_DHT_PutHandle *ret;
250
251   expire = GNUNET_GNSRECORD_record_get_expiration_time(rd_public_count,
252                                                        rd_public);
253   if (cache_keys)
254     block = GNUNET_GNSRECORD_block_create2(key,
255                                            expire,
256                                            label,
257                                            rd_public,
258                                            rd_public_count);
259   else
260     block = GNUNET_GNSRECORD_block_create(key,
261                                           expire,
262                                           label,
263                                           rd_public,
264                                           rd_public_count);
265   if (NULL == block)
266     {
267       GNUNET_break(0);
268       return NULL; /* whoops */
269     }
270   block_size = ntohl(block->purpose.size)
271                + sizeof(struct GNUNET_CRYPTO_EcdsaSignature)
272                + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey);
273   GNUNET_GNSRECORD_query_from_private_key(key,
274                                           label,
275                                           &query);
276   GNUNET_STATISTICS_update(statistics,
277                            "DHT put operations initiated",
278                            1,
279                            GNUNET_NO);
280   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
281              "Storing %u record(s) for label `%s' in DHT with expiration `%s' under key %s\n",
282              rd_public_count,
283              label,
284              GNUNET_STRINGS_absolute_time_to_string(expire),
285              GNUNET_h2s(&query));
286   ret = GNUNET_DHT_put(dht_handle,
287                        &query,
288                        DHT_GNS_REPLICATION_LEVEL,
289                        GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
290                        GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
291                        block_size,
292                        block,
293                        expire,
294                        &dht_put_monitor_continuation,
295                        ma);
296   GNUNET_free(block);
297   return ret;
298 }
299
300
301 /**
302  * Process a record that was stored in the namestore
303  * (invoked by the monitor).
304  *
305  * @param cls closure, NULL
306  * @param zone private key of the zone; NULL on disconnect
307  * @param label label of the records; NULL on disconnect
308  * @param rd_count number of entries in @a rd array, 0 if label was deleted
309  * @param rd array of records with data to store
310  */
311 static void
312 handle_monitor_event(void *cls,
313                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
314                      const char *label,
315                      unsigned int rd_count,
316                      const struct GNUNET_GNSRECORD_Data *rd)
317 {
318   struct GNUNET_GNSRECORD_Data rd_public[rd_count];
319   unsigned int rd_public_count;
320   struct DhtPutActivity *ma;
321
322   (void)cls;
323   GNUNET_STATISTICS_update(statistics,
324                            "Namestore monitor events received",
325                            1,
326                            GNUNET_NO);
327   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
328              "Received %u records for label `%s' via namestore monitor\n",
329              rd_count,
330              label);
331   /* filter out records that are not public, and convert to
332      absolute expiration time. */
333   rd_public_count = convert_records_for_export(rd,
334                                                rd_count,
335                                                rd_public);
336   if (0 == rd_public_count)
337     {
338       GNUNET_NAMESTORE_zone_monitor_next(zmon,
339                                          1);
340       return; /* nothing to do */
341     }
342   ma = GNUNET_new(struct DhtPutActivity);
343   ma->start_date = GNUNET_TIME_absolute_get();
344   ma->ph = perform_dht_put(zone,
345                            label,
346                            rd,
347                            rd_count,
348                            ma);
349   if (NULL == ma->ph)
350     {
351       /* PUT failed, do not remember operation */
352       GNUNET_free(ma);
353       GNUNET_NAMESTORE_zone_monitor_next(zmon,
354                                          1);
355       return;
356     }
357   GNUNET_CONTAINER_DLL_insert_tail(ma_head,
358                                    ma_tail,
359                                    ma);
360   ma_queue_length++;
361   if (ma_queue_length > DHT_QUEUE_LIMIT)
362     {
363       ma = ma_head;
364       GNUNET_CONTAINER_DLL_remove(ma_head,
365                                   ma_tail,
366                                   ma);
367       GNUNET_DHT_put_cancel(ma->ph);
368       ma_queue_length--;
369       GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
370                  "DHT PUT unconfirmed after %s, aborting PUT\n",
371                  GNUNET_STRINGS_relative_time_to_string(GNUNET_TIME_absolute_get_duration(ma->start_date),
372                                                         GNUNET_YES));
373       GNUNET_free(ma);
374     }
375 }
376
377
378 /**
379  * The zone monitor encountered an IPC error trying to to get in
380  * sync. Restart from the beginning.
381  *
382  * @param cls NULL
383  */
384 static void
385 handle_monitor_error(void *cls)
386 {
387   (void)cls;
388   GNUNET_STATISTICS_update(statistics,
389                            "Namestore monitor errors encountered",
390                            1,
391                            GNUNET_NO);
392 }
393
394
395 /**
396  * Performe zonemaster duties: watch namestore, publish records.
397  *
398  * @param cls closure
399  * @param server the initialized server
400  * @param c configuration to use
401  */
402 static void
403 run(void *cls,
404     const struct GNUNET_CONFIGURATION_Handle *c,
405     struct GNUNET_SERVICE_Handle *service)
406 {
407   unsigned long long max_parallel_bg_queries = 128;
408
409   (void)cls;
410   (void)service;
411   namestore_handle = GNUNET_NAMESTORE_connect(c);
412   if (NULL == namestore_handle)
413     {
414       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
415                  _("Failed to connect to the namestore!\n"));
416       GNUNET_SCHEDULER_shutdown();
417       return;
418     }
419   cache_keys = GNUNET_CONFIGURATION_get_value_yesno(c,
420                                                     "namestore",
421                                                     "CACHE_KEYS");
422   if (GNUNET_OK ==
423       GNUNET_CONFIGURATION_get_value_number(c,
424                                             "zonemaster",
425                                             "MAX_PARALLEL_BACKGROUND_QUERIES",
426                                             &max_parallel_bg_queries))
427     {
428       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
429                  "Number of allowed parallel background queries: %llu\n",
430                  max_parallel_bg_queries);
431     }
432   if (0 == max_parallel_bg_queries)
433     max_parallel_bg_queries = 1;
434   dht_handle = GNUNET_DHT_connect(c,
435                                   (unsigned int)max_parallel_bg_queries);
436   if (NULL == dht_handle)
437     {
438       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
439                  _("Could not connect to DHT!\n"));
440       GNUNET_SCHEDULER_add_now(&shutdown_task,
441                                NULL);
442       return;
443     }
444
445   /* Schedule periodic put for our records. */
446   statistics = GNUNET_STATISTICS_create("zonemaster-mon",
447                                         c);
448   zmon = GNUNET_NAMESTORE_zone_monitor_start(c,
449                                              NULL,
450                                              GNUNET_NO,
451                                              &handle_monitor_error,
452                                              NULL,
453                                              &handle_monitor_event,
454                                              NULL,
455                                              NULL /* sync_cb */,
456                                              NULL);
457   GNUNET_NAMESTORE_zone_monitor_next(zmon,
458                                      NAMESTORE_QUEUE_LIMIT - 1);
459   GNUNET_break(NULL != zmon);
460   GNUNET_SCHEDULER_add_shutdown(&shutdown_task,
461                                 NULL);
462 }
463
464
465 /**
466  * Define "main" method using service macro.
467  */
468 GNUNET_SERVICE_MAIN
469   ("zonemaster-monitor",
470   GNUNET_SERVICE_OPTION_NONE,
471   &run,
472   NULL,
473   NULL,
474   NULL,
475   GNUNET_MQ_handler_end());
476
477
478 /* end of gnunet-service-zonemaster-monitor.c */