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/>.
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
25 #include "gnunet_util_lib.h"
26 #include "gnunet_dht_service.h"
27 #include "gnunet_namestore_service.h"
28 #include "gnunet_statistics_service.h"
31 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
35 * How often should we (re)publish each record before
38 #define PUBLISH_OPS_PER_EXPIRATION 4
41 * How many pending DHT operations do we allow at most?
43 #define DHT_QUEUE_LIMIT 2000
46 * How many events may the namestore give us before it has to wait
49 #define NAMESTORE_QUEUE_LIMIT 5
52 * What replication level do we use for DHT PUT operations?
54 #define DHT_GNS_REPLICATION_LEVEL 5
58 * Handle for DHT PUT activity triggered from the namestore monitor.
65 struct DhtPutActivity *next;
70 struct DhtPutActivity *prev;
73 * Handle for the DHT PUT operation.
75 struct GNUNET_DHT_PutHandle *ph;
78 * When was this PUT initiated?
80 struct GNUNET_TIME_Absolute start_date;
85 * Handle to the statistics service
87 static struct GNUNET_STATISTICS_Handle *statistics;
90 * Our handle to the DHT
92 static struct GNUNET_DHT_Handle *dht_handle;
95 * Our handle to the namestore service
97 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
100 * Handle to monitor namestore changes to instant propagation.
102 static struct GNUNET_NAMESTORE_ZoneMonitor *zmon;
105 * Head of monitor activities; kept in a DLL.
107 static struct DhtPutActivity *ma_head;
110 * Tail of monitor activities; kept in a DLL.
112 static struct DhtPutActivity *ma_tail;
115 * Number of entries in the DHT queue #ma_head.
117 static unsigned int ma_queue_length;
120 * Optimize block insertion by caching map of private keys to
121 * public keys in memory?
123 static int cache_keys;
127 * Task run during shutdown.
133 shutdown_task (void *cls)
135 struct DhtPutActivity *ma;
138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
140 while (NULL != (ma = ma_head))
142 GNUNET_DHT_put_cancel (ma->ph);
144 GNUNET_CONTAINER_DLL_remove (ma_head,
149 if (NULL != statistics)
151 GNUNET_STATISTICS_destroy (statistics,
157 GNUNET_NAMESTORE_zone_monitor_stop (zmon);
160 if (NULL != namestore_handle)
162 GNUNET_NAMESTORE_disconnect (namestore_handle);
163 namestore_handle = NULL;
165 if (NULL != dht_handle)
167 GNUNET_DHT_disconnect (dht_handle);
174 * Continuation called from DHT once the PUT operation triggered
175 * by a monitor is done.
177 * @param cls a `struct DhtPutActivity`
180 dht_put_monitor_continuation (void *cls)
182 struct DhtPutActivity *ma = cls;
184 GNUNET_NAMESTORE_zone_monitor_next (zmon,
187 GNUNET_CONTAINER_DLL_remove (ma_head,
195 * Convert namestore records from the internal format to that
196 * suitable for publication (removes private records, converts
197 * to absolute expiration time).
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
205 convert_records_for_export (const struct GNUNET_GNSRECORD_Data *rd,
206 unsigned int rd_count,
207 struct GNUNET_GNSRECORD_Data *rd_public)
209 struct GNUNET_TIME_Absolute now;
210 unsigned int rd_public_count;
213 now = GNUNET_TIME_absolute_get ();
214 for (unsigned int i=0;i<rd_count;i++)
216 if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE))
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];
223 return rd_public_count;
228 * Store GNS records in the DHT.
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
237 static struct GNUNET_DHT_PutHandle *
238 perform_dht_put (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
240 const struct GNUNET_GNSRECORD_Data *rd_public,
241 unsigned int rd_public_count,
242 struct DhtPutActivity *ma)
244 struct GNUNET_GNSRECORD_Block *block;
245 struct GNUNET_HashCode query;
246 struct GNUNET_TIME_Absolute expire;
248 struct GNUNET_DHT_PutHandle *ret;
250 expire = GNUNET_GNSRECORD_record_get_expiration_time (rd_public_count,
253 block = GNUNET_GNSRECORD_block_create2 (key,
259 block = GNUNET_GNSRECORD_block_create (key,
267 return NULL; /* whoops */
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,
275 GNUNET_STATISTICS_update (statistics,
276 "DHT put operations initiated",
279 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
280 "Storing %u record(s) for label `%s' in DHT with expiration `%s' under key %s\n",
283 GNUNET_STRINGS_absolute_time_to_string (expire),
284 GNUNET_h2s (&query));
285 ret = GNUNET_DHT_put (dht_handle,
287 DHT_GNS_REPLICATION_LEVEL,
288 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
289 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
293 &dht_put_monitor_continuation,
301 * Process a record that was stored in the namestore
302 * (invoked by the monitor).
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
311 handle_monitor_event (void *cls,
312 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
314 unsigned int rd_count,
315 const struct GNUNET_GNSRECORD_Data *rd)
317 struct GNUNET_GNSRECORD_Data rd_public[rd_count];
318 unsigned int rd_public_count;
319 struct DhtPutActivity *ma;
322 GNUNET_STATISTICS_update (statistics,
323 "Namestore monitor events received",
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327 "Received %u records for label `%s' via namestore monitor\n",
330 /* filter out records that are not public, and convert to
331 absolute expiration time. */
332 rd_public_count = convert_records_for_export (rd,
335 if (0 == rd_public_count)
337 GNUNET_NAMESTORE_zone_monitor_next (zmon,
339 return; /* nothing to do */
341 ma = GNUNET_new (struct DhtPutActivity);
342 ma->start_date = GNUNET_TIME_absolute_get ();
343 ma->ph = perform_dht_put (zone,
350 /* PUT failed, do not remember operation */
352 GNUNET_NAMESTORE_zone_monitor_next (zmon,
356 GNUNET_CONTAINER_DLL_insert_tail (ma_head,
360 if (ma_queue_length > DHT_QUEUE_LIMIT)
363 GNUNET_CONTAINER_DLL_remove (ma_head,
366 GNUNET_DHT_put_cancel (ma->ph);
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),
378 * The zone monitor encountered an IPC error trying to to get in
379 * sync. Restart from the beginning.
384 handle_monitor_error (void *cls)
387 GNUNET_STATISTICS_update (statistics,
388 "Namestore monitor errors encountered",
395 * Performe zonemaster duties: watch namestore, publish records.
398 * @param server the initialized server
399 * @param c configuration to use
403 const struct GNUNET_CONFIGURATION_Handle *c,
404 struct GNUNET_SERVICE_Handle *service)
406 unsigned long long max_parallel_bg_queries = 128;
410 namestore_handle = GNUNET_NAMESTORE_connect (c);
411 if (NULL == namestore_handle)
413 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
414 _("Failed to connect to the namestore!\n"));
415 GNUNET_SCHEDULER_shutdown ();
418 cache_keys = GNUNET_CONFIGURATION_get_value_yesno (c,
422 GNUNET_CONFIGURATION_get_value_number (c,
424 "MAX_PARALLEL_BACKGROUND_QUERIES",
425 &max_parallel_bg_queries))
427 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428 "Number of allowed parallel background queries: %llu\n",
429 max_parallel_bg_queries);
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)
437 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
438 _("Could not connect to DHT!\n"));
439 GNUNET_SCHEDULER_add_now (&shutdown_task,
444 /* Schedule periodic put for our records. */
445 statistics = GNUNET_STATISTICS_create ("zonemaster-mon",
447 zmon = GNUNET_NAMESTORE_zone_monitor_start (c,
450 &handle_monitor_error,
452 &handle_monitor_event,
456 GNUNET_NAMESTORE_zone_monitor_next (zmon,
457 NAMESTORE_QUEUE_LIMIT - 1);
458 GNUNET_break (NULL != zmon);
459 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
465 * Define "main" method using service macro.
468 ("zonemaster-monitor",
469 GNUNET_SERVICE_OPTION_NONE,
474 GNUNET_MQ_handler_end());
477 /* end of gnunet-service-zonemaster-monitor.c */