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