2 This file is part of GNUnet.
3 Copyright (C) 2012, 2013, 2014, 2017, 2018 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dht_service.h"
29 #include "gnunet_namestore_service.h"
30 #include "gnunet_statistics_service.h"
33 #define LOG_STRERROR_FILE(kind, syscall, filename) GNUNET_log_from_strerror_file(kind, "util", syscall, filename)
37 * How often should we (re)publish each record before
40 #define PUBLISH_OPS_PER_EXPIRATION 4
43 * How many pending DHT operations do we allow at most?
45 #define DHT_QUEUE_LIMIT 2000
48 * How many events may the namestore give us before it has to wait
51 #define NAMESTORE_QUEUE_LIMIT 5
54 * What replication level do we use for DHT PUT operations?
56 #define DHT_GNS_REPLICATION_LEVEL 5
60 * Handle for DHT PUT activity triggered from the namestore monitor.
62 struct DhtPutActivity {
66 struct DhtPutActivity *next;
71 struct DhtPutActivity *prev;
74 * Handle for the DHT PUT operation.
76 struct GNUNET_DHT_PutHandle *ph;
79 * When was this PUT initiated?
81 struct GNUNET_TIME_Absolute start_date;
86 * Handle to the statistics service
88 static struct GNUNET_STATISTICS_Handle *statistics;
91 * Our handle to the DHT
93 static struct GNUNET_DHT_Handle *dht_handle;
96 * Our handle to the namestore service
98 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
101 * Handle to monitor namestore changes to instant propagation.
103 static struct GNUNET_NAMESTORE_ZoneMonitor *zmon;
106 * Head of monitor activities; kept in a DLL.
108 static struct DhtPutActivity *ma_head;
111 * Tail of monitor activities; kept in a DLL.
113 static struct DhtPutActivity *ma_tail;
116 * Number of entries in the DHT queue #ma_head.
118 static unsigned int ma_queue_length;
121 * Optimize block insertion by caching map of private keys to
122 * public keys in memory?
124 static int cache_keys;
128 * Task run during shutdown.
134 shutdown_task(void *cls)
136 struct DhtPutActivity *ma;
139 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
141 while (NULL != (ma = ma_head))
143 GNUNET_DHT_put_cancel(ma->ph);
145 GNUNET_CONTAINER_DLL_remove(ma_head,
150 if (NULL != statistics)
152 GNUNET_STATISTICS_destroy(statistics,
158 GNUNET_NAMESTORE_zone_monitor_stop(zmon);
161 if (NULL != namestore_handle)
163 GNUNET_NAMESTORE_disconnect(namestore_handle);
164 namestore_handle = NULL;
166 if (NULL != dht_handle)
168 GNUNET_DHT_disconnect(dht_handle);
175 * Continuation called from DHT once the PUT operation triggered
176 * by a monitor is done.
178 * @param cls a `struct DhtPutActivity`
181 dht_put_monitor_continuation(void *cls)
183 struct DhtPutActivity *ma = cls;
185 GNUNET_NAMESTORE_zone_monitor_next(zmon,
188 GNUNET_CONTAINER_DLL_remove(ma_head,
196 * Convert namestore records from the internal format to that
197 * suitable for publication (removes private records, converts
198 * to absolute expiration time).
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
206 convert_records_for_export(const struct GNUNET_GNSRECORD_Data *rd,
207 unsigned int rd_count,
208 struct GNUNET_GNSRECORD_Data *rd_public)
210 struct GNUNET_TIME_Absolute now;
211 unsigned int rd_public_count;
214 now = GNUNET_TIME_absolute_get();
215 for (unsigned int i = 0; i < rd_count; i++)
217 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE))
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];
224 return rd_public_count;
229 * Store GNS records in the DHT.
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
238 static struct GNUNET_DHT_PutHandle *
239 perform_dht_put(const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
241 const struct GNUNET_GNSRECORD_Data *rd_public,
242 unsigned int rd_public_count,
243 struct DhtPutActivity *ma)
245 struct GNUNET_GNSRECORD_Block *block;
246 struct GNUNET_HashCode query;
247 struct GNUNET_TIME_Absolute expire;
249 struct GNUNET_DHT_PutHandle *ret;
251 expire = GNUNET_GNSRECORD_record_get_expiration_time(rd_public_count,
254 block = GNUNET_GNSRECORD_block_create2(key,
260 block = GNUNET_GNSRECORD_block_create(key,
268 return NULL; /* whoops */
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,
276 GNUNET_STATISTICS_update(statistics,
277 "DHT put operations initiated",
280 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
281 "Storing %u record(s) for label `%s' in DHT with expiration `%s' under key %s\n",
284 GNUNET_STRINGS_absolute_time_to_string(expire),
286 ret = GNUNET_DHT_put(dht_handle,
288 DHT_GNS_REPLICATION_LEVEL,
289 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
290 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
294 &dht_put_monitor_continuation,
302 * Process a record that was stored in the namestore
303 * (invoked by the monitor).
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
312 handle_monitor_event(void *cls,
313 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
315 unsigned int rd_count,
316 const struct GNUNET_GNSRECORD_Data *rd)
318 struct GNUNET_GNSRECORD_Data rd_public[rd_count];
319 unsigned int rd_public_count;
320 struct DhtPutActivity *ma;
323 GNUNET_STATISTICS_update(statistics,
324 "Namestore monitor events received",
327 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
328 "Received %u records for label `%s' via namestore monitor\n",
331 /* filter out records that are not public, and convert to
332 absolute expiration time. */
333 rd_public_count = convert_records_for_export(rd,
336 if (0 == rd_public_count)
338 GNUNET_NAMESTORE_zone_monitor_next(zmon,
340 return; /* nothing to do */
342 ma = GNUNET_new(struct DhtPutActivity);
343 ma->start_date = GNUNET_TIME_absolute_get();
344 ma->ph = perform_dht_put(zone,
351 /* PUT failed, do not remember operation */
353 GNUNET_NAMESTORE_zone_monitor_next(zmon,
357 GNUNET_CONTAINER_DLL_insert_tail(ma_head,
361 if (ma_queue_length > DHT_QUEUE_LIMIT)
364 GNUNET_CONTAINER_DLL_remove(ma_head,
367 GNUNET_DHT_put_cancel(ma->ph);
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),
379 * The zone monitor encountered an IPC error trying to to get in
380 * sync. Restart from the beginning.
385 handle_monitor_error(void *cls)
388 GNUNET_STATISTICS_update(statistics,
389 "Namestore monitor errors encountered",
396 * Performe zonemaster duties: watch namestore, publish records.
399 * @param server the initialized server
400 * @param c configuration to use
404 const struct GNUNET_CONFIGURATION_Handle *c,
405 struct GNUNET_SERVICE_Handle *service)
407 unsigned long long max_parallel_bg_queries = 128;
411 namestore_handle = GNUNET_NAMESTORE_connect(c);
412 if (NULL == namestore_handle)
414 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
415 _("Failed to connect to the namestore!\n"));
416 GNUNET_SCHEDULER_shutdown();
419 cache_keys = GNUNET_CONFIGURATION_get_value_yesno(c,
423 GNUNET_CONFIGURATION_get_value_number(c,
425 "MAX_PARALLEL_BACKGROUND_QUERIES",
426 &max_parallel_bg_queries))
428 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
429 "Number of allowed parallel background queries: %llu\n",
430 max_parallel_bg_queries);
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)
438 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
439 _("Could not connect to DHT!\n"));
440 GNUNET_SCHEDULER_add_now(&shutdown_task,
445 /* Schedule periodic put for our records. */
446 statistics = GNUNET_STATISTICS_create("zonemaster-mon",
448 zmon = GNUNET_NAMESTORE_zone_monitor_start(c,
451 &handle_monitor_error,
453 &handle_monitor_event,
457 GNUNET_NAMESTORE_zone_monitor_next(zmon,
458 NAMESTORE_QUEUE_LIMIT - 1);
459 GNUNET_break(NULL != zmon);
460 GNUNET_SCHEDULER_add_shutdown(&shutdown_task,
466 * Define "main" method using service macro.
469 ("zonemaster-monitor",
470 GNUNET_SERVICE_OPTION_NONE,
475 GNUNET_MQ_handler_end());
478 /* end of gnunet-service-zonemaster-monitor.c */