From b7a1d4379ebcff1c878d068cbd8df34fd16d81d4 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Thu, 19 Oct 2023 09:37:26 +0200 Subject: BUILD: Move namestore to service --- src/Makefile.am | 1 - src/cli/Makefile.am | 1 + src/cli/namestore/.gitignore | 6 + src/cli/namestore/Makefile.am | 71 + src/cli/namestore/example_zonefile | 27 + src/cli/namestore/gnunet-namestore-dbtool.c | 199 ++ src/cli/namestore/gnunet-namestore-zonefile.c | 763 ++++++ src/cli/namestore/gnunet-namestore.c | 2120 +++++++++++++++ src/cli/namestore/gnunet-zoneimport.c | 1872 +++++++++++++ src/cli/namestore/test_namestore_delete.sh | 68 + src/cli/namestore/test_namestore_lookup.sh | 63 + src/cli/namestore/test_namestore_put.sh | 55 + src/cli/namestore/test_namestore_put_multiple.sh | 112 + src/cli/namestore/test_namestore_put_stdin.sh | 68 + .../namestore/test_namestore_zonefile_import.sh | 33 + src/contrib/service/abd/Makefile.am | 4 +- src/conversation/Makefile.am | 10 +- src/gns/Makefile.am | 2 +- src/namestore/.gitignore | 61 - src/namestore/Makefile.am | 543 ---- src/namestore/example_zonefile | 27 - src/namestore/gnunet-namestore-dbtool.c | 199 -- src/namestore/gnunet-namestore-fcfsd.c | 1160 --------- src/namestore/gnunet-namestore-zonefile.c | 763 ------ src/namestore/gnunet-namestore.c | 2120 --------------- src/namestore/gnunet-service-namestore.c | 2752 -------------------- src/namestore/gnunet-zoneimport.c | 1872 ------------- src/namestore/meson.build | 139 - src/namestore/namestore-0001.sql | 48 - src/namestore/namestore-drop.sql | 25 - src/namestore/namestore.conf.in | 47 - src/namestore/namestore.h | 491 ---- src/namestore/namestore_api.c | 1563 ----------- src/namestore/namestore_api_monitor.c | 443 ---- src/namestore/perf_namestore_api_import.c | 406 --- src/namestore/perf_namestore_api_postgres.conf | 12 - src/namestore/perf_namestore_api_sqlite.conf | 8 - src/namestore/perf_namestore_api_zone_iteration.c | 378 --- src/namestore/plugin_namestore_flat.c | 818 ------ src/namestore/plugin_namestore_postgres.c | 796 ------ src/namestore/plugin_namestore_sqlite.c | 983 ------- src/namestore/plugin_rest_namestore.c | 1364 ---------- src/namestore/test_common.c | 128 - src/namestore/test_hostkey | 0 src/namestore/test_namestore_api.conf | 43 - src/namestore/test_namestore_api_edit_records.c | 399 --- src/namestore/test_namestore_api_lookup_nick.c | 347 --- src/namestore/test_namestore_api_monitoring.c | 378 --- .../test_namestore_api_monitoring_existing.c | 393 --- src/namestore/test_namestore_api_postgres.conf | 10 - src/namestore/test_namestore_api_remove.c | 219 -- ...test_namestore_api_remove_not_existing_record.c | 179 -- src/namestore/test_namestore_api_sqlite.conf | 9 - src/namestore/test_namestore_api_store.c | 172 -- src/namestore/test_namestore_api_store_update.c | 269 -- src/namestore/test_namestore_api_tx_rollback.c | 264 -- src/namestore/test_namestore_api_zone_iteration.c | 464 ---- .../test_namestore_api_zone_iteration_nick.c | 460 ---- ...st_namestore_api_zone_iteration_specific_zone.c | 447 ---- .../test_namestore_api_zone_iteration_stop.c | 449 ---- src/namestore/test_namestore_api_zone_to_name.c | 266 -- src/namestore/test_namestore_delete.sh | 68 - src/namestore/test_namestore_lookup.sh | 63 - src/namestore/test_namestore_put.sh | 55 - src/namestore/test_namestore_put_multiple.sh | 112 - src/namestore/test_namestore_put_stdin.sh | 68 - src/namestore/test_namestore_zonefile_import.sh | 33 - src/namestore/test_plugin_namestore.c | 218 -- src/namestore/test_plugin_namestore_postgres.conf | 4 - src/namestore/test_plugin_namestore_sqlite.conf | 3 - src/namestore/test_plugin_rest_namestore.sh | 131 - src/plugin/Makefile.am | 3 +- src/plugin/namestore/Makefile.am | 439 ++++ src/plugin/namestore/namestore-0001.sql | 48 + src/plugin/namestore/namestore-drop.sql | 25 + src/plugin/namestore/perf_namestore_api_import.c | 406 +++ .../namestore/perf_namestore_api_postgres.conf | 12 + .../namestore/perf_namestore_api_sqlite.conf | 8 + .../namestore/perf_namestore_api_zone_iteration.c | 378 +++ src/plugin/namestore/plugin_namestore_flat.c | 817 ++++++ src/plugin/namestore/plugin_namestore_postgres.c | 795 ++++++ src/plugin/namestore/plugin_namestore_sqlite.c | 982 +++++++ src/plugin/namestore/plugin_rest_namestore.c | 1364 ++++++++++ src/plugin/namestore/test_common.c | 128 + src/plugin/namestore/test_hostkey | 0 src/plugin/namestore/test_namestore_api.conf | 43 + .../namestore/test_namestore_api_edit_records.c | 399 +++ .../namestore/test_namestore_api_lookup_nick.c | 347 +++ .../namestore/test_namestore_api_monitoring.c | 378 +++ .../test_namestore_api_monitoring_existing.c | 393 +++ .../namestore/test_namestore_api_postgres.conf | 10 + src/plugin/namestore/test_namestore_api_remove.c | 219 ++ ...test_namestore_api_remove_not_existing_record.c | 179 ++ .../namestore/test_namestore_api_sqlite.conf | 9 + src/plugin/namestore/test_namestore_api_store.c | 172 ++ .../namestore/test_namestore_api_store_update.c | 269 ++ .../namestore/test_namestore_api_tx_rollback.c | 264 ++ .../namestore/test_namestore_api_zone_iteration.c | 464 ++++ .../test_namestore_api_zone_iteration_nick.c | 460 ++++ ...st_namestore_api_zone_iteration_specific_zone.c | 447 ++++ .../test_namestore_api_zone_iteration_stop.c | 449 ++++ .../namestore/test_namestore_api_zone_to_name.c | 266 ++ src/plugin/namestore/test_plugin_namestore.c | 218 ++ .../namestore/test_plugin_namestore_postgres.conf | 4 + .../namestore/test_plugin_namestore_sqlite.conf | 3 + src/plugin/namestore/test_plugin_rest_namestore.sh | 131 + src/pt/Makefile.am | 2 +- src/reclaim/Makefile.am | 14 +- src/service/Makefile.am | 1 + src/service/namestore/.gitignore | 57 + src/service/namestore/Makefile.am | 63 + src/service/namestore/gnunet-namestore-fcfsd.c | 1160 +++++++++ src/service/namestore/gnunet-service-namestore.c | 2752 ++++++++++++++++++++ src/service/namestore/meson.build | 139 + src/service/namestore/namestore.conf.in | 47 + src/service/namestore/namestore.h | 491 ++++ src/service/namestore/namestore_api.c | 1563 +++++++++++ src/service/namestore/namestore_api_monitor.c | 443 ++++ src/zonemaster/Makefile.am | 2 +- 119 files changed, 22719 insertions(+), 22688 deletions(-) create mode 100644 src/cli/namestore/.gitignore create mode 100644 src/cli/namestore/Makefile.am create mode 100644 src/cli/namestore/example_zonefile create mode 100644 src/cli/namestore/gnunet-namestore-dbtool.c create mode 100644 src/cli/namestore/gnunet-namestore-zonefile.c create mode 100644 src/cli/namestore/gnunet-namestore.c create mode 100644 src/cli/namestore/gnunet-zoneimport.c create mode 100755 src/cli/namestore/test_namestore_delete.sh create mode 100755 src/cli/namestore/test_namestore_lookup.sh create mode 100755 src/cli/namestore/test_namestore_put.sh create mode 100755 src/cli/namestore/test_namestore_put_multiple.sh create mode 100755 src/cli/namestore/test_namestore_put_stdin.sh create mode 100755 src/cli/namestore/test_namestore_zonefile_import.sh delete mode 100644 src/namestore/.gitignore delete mode 100644 src/namestore/Makefile.am delete mode 100644 src/namestore/example_zonefile delete mode 100644 src/namestore/gnunet-namestore-dbtool.c delete mode 100644 src/namestore/gnunet-namestore-fcfsd.c delete mode 100644 src/namestore/gnunet-namestore-zonefile.c delete mode 100644 src/namestore/gnunet-namestore.c delete mode 100644 src/namestore/gnunet-service-namestore.c delete mode 100644 src/namestore/gnunet-zoneimport.c delete mode 100644 src/namestore/meson.build delete mode 100644 src/namestore/namestore-0001.sql delete mode 100644 src/namestore/namestore-drop.sql delete mode 100644 src/namestore/namestore.conf.in delete mode 100644 src/namestore/namestore.h delete mode 100644 src/namestore/namestore_api.c delete mode 100644 src/namestore/namestore_api_monitor.c delete mode 100644 src/namestore/perf_namestore_api_import.c delete mode 100644 src/namestore/perf_namestore_api_postgres.conf delete mode 100644 src/namestore/perf_namestore_api_sqlite.conf delete mode 100644 src/namestore/perf_namestore_api_zone_iteration.c delete mode 100644 src/namestore/plugin_namestore_flat.c delete mode 100644 src/namestore/plugin_namestore_postgres.c delete mode 100644 src/namestore/plugin_namestore_sqlite.c delete mode 100644 src/namestore/plugin_rest_namestore.c delete mode 100644 src/namestore/test_common.c delete mode 100644 src/namestore/test_hostkey delete mode 100644 src/namestore/test_namestore_api.conf delete mode 100644 src/namestore/test_namestore_api_edit_records.c delete mode 100644 src/namestore/test_namestore_api_lookup_nick.c delete mode 100644 src/namestore/test_namestore_api_monitoring.c delete mode 100644 src/namestore/test_namestore_api_monitoring_existing.c delete mode 100644 src/namestore/test_namestore_api_postgres.conf delete mode 100644 src/namestore/test_namestore_api_remove.c delete mode 100644 src/namestore/test_namestore_api_remove_not_existing_record.c delete mode 100644 src/namestore/test_namestore_api_sqlite.conf delete mode 100644 src/namestore/test_namestore_api_store.c delete mode 100644 src/namestore/test_namestore_api_store_update.c delete mode 100644 src/namestore/test_namestore_api_tx_rollback.c delete mode 100644 src/namestore/test_namestore_api_zone_iteration.c delete mode 100644 src/namestore/test_namestore_api_zone_iteration_nick.c delete mode 100644 src/namestore/test_namestore_api_zone_iteration_specific_zone.c delete mode 100644 src/namestore/test_namestore_api_zone_iteration_stop.c delete mode 100644 src/namestore/test_namestore_api_zone_to_name.c delete mode 100755 src/namestore/test_namestore_delete.sh delete mode 100755 src/namestore/test_namestore_lookup.sh delete mode 100755 src/namestore/test_namestore_put.sh delete mode 100755 src/namestore/test_namestore_put_multiple.sh delete mode 100755 src/namestore/test_namestore_put_stdin.sh delete mode 100755 src/namestore/test_namestore_zonefile_import.sh delete mode 100644 src/namestore/test_plugin_namestore.c delete mode 100644 src/namestore/test_plugin_namestore_postgres.conf delete mode 100644 src/namestore/test_plugin_namestore_sqlite.conf delete mode 100755 src/namestore/test_plugin_rest_namestore.sh create mode 100644 src/plugin/namestore/Makefile.am create mode 100644 src/plugin/namestore/namestore-0001.sql create mode 100644 src/plugin/namestore/namestore-drop.sql create mode 100644 src/plugin/namestore/perf_namestore_api_import.c create mode 100644 src/plugin/namestore/perf_namestore_api_postgres.conf create mode 100644 src/plugin/namestore/perf_namestore_api_sqlite.conf create mode 100644 src/plugin/namestore/perf_namestore_api_zone_iteration.c create mode 100644 src/plugin/namestore/plugin_namestore_flat.c create mode 100644 src/plugin/namestore/plugin_namestore_postgres.c create mode 100644 src/plugin/namestore/plugin_namestore_sqlite.c create mode 100644 src/plugin/namestore/plugin_rest_namestore.c create mode 100644 src/plugin/namestore/test_common.c create mode 100644 src/plugin/namestore/test_hostkey create mode 100644 src/plugin/namestore/test_namestore_api.conf create mode 100644 src/plugin/namestore/test_namestore_api_edit_records.c create mode 100644 src/plugin/namestore/test_namestore_api_lookup_nick.c create mode 100644 src/plugin/namestore/test_namestore_api_monitoring.c create mode 100644 src/plugin/namestore/test_namestore_api_monitoring_existing.c create mode 100644 src/plugin/namestore/test_namestore_api_postgres.conf create mode 100644 src/plugin/namestore/test_namestore_api_remove.c create mode 100644 src/plugin/namestore/test_namestore_api_remove_not_existing_record.c create mode 100644 src/plugin/namestore/test_namestore_api_sqlite.conf create mode 100644 src/plugin/namestore/test_namestore_api_store.c create mode 100644 src/plugin/namestore/test_namestore_api_store_update.c create mode 100644 src/plugin/namestore/test_namestore_api_tx_rollback.c create mode 100644 src/plugin/namestore/test_namestore_api_zone_iteration.c create mode 100644 src/plugin/namestore/test_namestore_api_zone_iteration_nick.c create mode 100644 src/plugin/namestore/test_namestore_api_zone_iteration_specific_zone.c create mode 100644 src/plugin/namestore/test_namestore_api_zone_iteration_stop.c create mode 100644 src/plugin/namestore/test_namestore_api_zone_to_name.c create mode 100644 src/plugin/namestore/test_plugin_namestore.c create mode 100644 src/plugin/namestore/test_plugin_namestore_postgres.conf create mode 100644 src/plugin/namestore/test_plugin_namestore_sqlite.conf create mode 100755 src/plugin/namestore/test_plugin_rest_namestore.sh create mode 100644 src/service/namestore/.gitignore create mode 100644 src/service/namestore/Makefile.am create mode 100644 src/service/namestore/gnunet-namestore-fcfsd.c create mode 100644 src/service/namestore/gnunet-service-namestore.c create mode 100644 src/service/namestore/meson.build create mode 100644 src/service/namestore/namestore.conf.in create mode 100644 src/service/namestore/namestore.h create mode 100644 src/service/namestore/namestore_api.c create mode 100644 src/service/namestore/namestore_api_monitor.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index d166fd788..208049b50 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,7 +17,6 @@ SUBDIRS = \ rest-plugin \ cli \ contrib \ - namestore \ set \ seti \ setu \ diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index 0235c0a1c..78ff8b07b 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -9,4 +9,5 @@ SUBDIRS = \ datastore \ dht \ namecache \ + namestore \ cadet diff --git a/src/cli/namestore/.gitignore b/src/cli/namestore/.gitignore new file mode 100644 index 000000000..3c0282e8c --- /dev/null +++ b/src/cli/namestore/.gitignore @@ -0,0 +1,6 @@ +gnunet-namestore +gnunet-namestore-dbtool +gnunet-namestore-zonefile +gnunet-namestore-fcfsd +gnunet-zoneimport + diff --git a/src/cli/namestore/Makefile.am b/src/cli/namestore/Makefile.am new file mode 100644 index 000000000..86ab2f0f7 --- /dev/null +++ b/src/cli/namestore/Makefile.am @@ -0,0 +1,71 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS) + +plugindir = $(libdir)/gnunet + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +sqldir = $(prefix)/share/gnunet/sql/ + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIBS = -lgcov +endif + + +bin_PROGRAMS = \ + gnunet-namestore \ + gnunet-namestore-dbtool \ + gnunet-namestore-zonefile \ + gnunet-zoneimport + +gnunet_namestore_zonefile_SOURCES = \ + gnunet-namestore-zonefile.c +gnunet_namestore_zonefile_LDADD = \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_zoneimport_SOURCES = \ + gnunet-zoneimport.c +gnunet_zoneimport_LDADD = \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_namestore_SOURCES = \ + gnunet-namestore.c +gnunet_namestore_LDADD = \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(GN_LIBINTL) + +gnunet_namestore_dbtool_SOURCES = \ + gnunet-namestore-dbtool.c +gnunet_namestore_dbtool_LDADD = \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(GN_LIBINTL) + + + +check_SCRIPTS = \ + test_namestore_put.sh \ + test_namestore_put_stdin.sh \ + test_namestore_lookup.sh \ + test_namestore_delete.sh \ + test_namestore_zonefile_import.sh + +EXTRA_DIST = \ + test_hostkey \ + example_zonefile \ + $(check_SCRIPTS) diff --git a/src/cli/namestore/example_zonefile b/src/cli/namestore/example_zonefile new file mode 100644 index 000000000..5e380ff90 --- /dev/null +++ b/src/cli/namestore/example_zonefile @@ -0,0 +1,27 @@ +$ORIGIN example.com. ; designates the start of this zone file in the namespace +$TTL 3600 ; default expiration time (in seconds) of all RRs without their own TTL value +example.com. IN SOA ns.example.com. username.example.com. ( 2020091025 ; A comment + 7200 ; Comment + ; empty line on purpose + 3600 + 1209600 + 3600 ) +example.com. IN NS ns ; ns.example.com is a nameserver for example.com +example.com. IN NS ns.somewhere.example. ; ns.somewhere.example is a backup nameserver for example.com +example.com. IN MX 10 mail.example.com. ; mail.example.com is the mailserver for example.com +@ IN MX 20 mail2.example.com. ; equivalent to above line, "@" represents zone origin +@ IN MX 50 mail3 ; equivalent to above line, but using a relative host name +b.example.com. IN A 192.0.2.1 ; IPv4 address for example.com + IN AAAA 2001:db8:10::1 ; IPv6 address for example.com +ns IN A 192.0.2.2 ; IPv4 address for ns.example.com + IN AAAA 2001:db8:10::2 ; IPv6 address for ns.example.com +www IN CNAME example.com. ; www.example.com is an alias for example.com +wwwtest IN CNAME www ; wwwtest.example.com is another alias for www.example.com +mail IN A 192.0.2.3 ; IPv4 address for mail.example.com +mail2 IN A 192.0.2.4 ; IPv4 address for mail2.example.com +mail3 IN A 192.0.2.5 ; IPv4 address for mail3.example.com + +mail3 IN TXT "This is ; quoted" ; A quoted comment separator +$ORIGIN example.de. +www2 IN A 192.0.2.7 ; IPv4 address for www2.example.de + diff --git a/src/cli/namestore/gnunet-namestore-dbtool.c b/src/cli/namestore/gnunet-namestore-dbtool.c new file mode 100644 index 000000000..835d7a228 --- /dev/null +++ b/src/cli/namestore/gnunet-namestore-dbtool.c @@ -0,0 +1,199 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012, 2013, 2014, 2019, 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file gnunet-namestore-dbtool.c + * @brief command line tool to manipulate the database backends for the namestore + * @author Martin Schanzenbach + * + */ +#include "platform.h" +#include +#include + +/** + * Name of the plugin argument + */ +static char *pluginname; + +/** + * Reset argument + */ +static int reset; + +/** + * Initialize argument + */ +static int init; + +/** + * Return code + */ +static int ret = 0; + +/** + * Task run on shutdown. Cleans up everything. + * + * @param cls unused + */ +static void +do_shutdown (void *cls) +{ + (void) cls; + if (NULL != pluginname) + GNUNET_free (pluginname); +} + + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *db_lib_name; + struct GNUNET_NAMESTORE_PluginFunctions *plugin; + + (void) cls; + (void) args; + (void) cfgfile; + if (NULL != args[0]) + GNUNET_log ( + GNUNET_ERROR_TYPE_WARNING, + _ ("Superfluous command line arguments (starting with `%s') ignored\n"), + args[0]); + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + (void *) cfg); + if (NULL == pluginname) + { + fprintf (stderr, "No plugin given!\n"); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_asprintf (&db_lib_name, + "libgnunet_plugin_namestore_%s", + pluginname); + plugin = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); + if (NULL == plugin) + { + fprintf (stderr, + "Failed to load %s!\n", + db_lib_name); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + GNUNET_free (db_lib_name); + return; + } + if (reset) + { + if (GNUNET_OK != + plugin->drop_tables (plugin->cls)) + { + fprintf (stderr, + "Failed to reset database\n"); + ret = 1; + GNUNET_free (db_lib_name); + GNUNET_SCHEDULER_shutdown (); + return; + } + } + if (init || reset) + { + if (GNUNET_OK != + plugin->create_tables (plugin->cls)) + { + fprintf (stderr, + "Failed to initialize database\n"); + ret = 1; + GNUNET_free (db_lib_name); + GNUNET_SCHEDULER_shutdown (); + return; + } + } + GNUNET_SCHEDULER_shutdown (); + GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, + plugin)); + GNUNET_free (db_lib_name); +} + + +/** + * The main function for gnunet-namestore-dbtool. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_flag ('i', "init", + gettext_noop ("initialize database"), + &init), + GNUNET_GETOPT_option_flag ('r', + "reset", + gettext_noop ( + "reset database (DANGEROUS: All existing data is lost!"), + &reset), + GNUNET_GETOPT_option_string ( + 'p', + "plugin", + "PLUGIN", + gettext_noop ( + "the namestore plugin to work with, e.g. 'sqlite'"), + &pluginname), + GNUNET_GETOPT_OPTION_END + }; + int lret; + + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return 2; + + GNUNET_log_setup ("gnunet-namestore-dbtool", + "WARNING", + NULL); + if (GNUNET_OK != + (lret = GNUNET_PROGRAM_run (argc, + argv, + "gnunet-namestore-dbtool", + _ ( + "GNUnet namestore database manipulation tool"), + options, + &run, + NULL))) + { + GNUNET_free_nz ((void *) argv); + return lret; + } + GNUNET_free_nz ((void *) argv); + return ret; +} diff --git a/src/cli/namestore/gnunet-namestore-zonefile.c b/src/cli/namestore/gnunet-namestore-zonefile.c new file mode 100644 index 000000000..dfd438e94 --- /dev/null +++ b/src/cli/namestore/gnunet-namestore-zonefile.c @@ -0,0 +1,763 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012, 2013, 2014, 2019, 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file gnunet-namestore-dbtool.c + * @brief command line tool to manipulate the database backends for the namestore + * @author Martin Schanzenbach + * + */ +#include "platform.h" +#include +#include + +#define MAX_RECORDS_PER_NAME 50 + +/** + * Maximum length of a zonefile line + */ +#define MAX_ZONEFILE_LINE_LEN 4096 + +/** + * FIXME: Soft limit this? + */ +#define MAX_ZONEFILE_RECORD_DATA_LEN 2048 + +/** + * The record data under a single label. Reused. + * Hard limit. + */ +static struct GNUNET_GNSRECORD_Data rd[MAX_RECORDS_PER_NAME]; + +/** + * Current record $TTL to use + */ +static struct GNUNET_TIME_Relative ttl; + +/** + * Current origin + */ +static char origin[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; + +/** + * Number of records for currently parsed set + */ +static unsigned int rd_count = 0; + +/** + * Return code + */ +static int ret = 0; + +/** + * Name of the ego + */ +static char *ego_name = NULL; + +/** + * Currently read line or NULL on EOF + */ +static char *res; + +/** + * Statistics, how many published record sets + */ +static unsigned int published_sets = 0; + +/** + * Statistics, how many records published in aggregate + */ +static unsigned int published_records = 0; + + +/** + * Handle to identity lookup. + */ +static struct GNUNET_IDENTITY_EgoLookup *el; + +/** + * Private key for the our zone. + */ +static struct GNUNET_CRYPTO_PrivateKey zone_pkey; + +/** + * Queue entry for the 'add' operation. + */ +static struct GNUNET_NAMESTORE_QueueEntry *ns_qe; + +/** + * Handle to the namestore. + */ +static struct GNUNET_NAMESTORE_Handle *ns; + +/** + * Origin create operations + */ +static struct GNUNET_IDENTITY_Operation *id_op; + +/** + * Handle to IDENTITY + */ +static struct GNUNET_IDENTITY_Handle *id; + +/** + * Current configurataion + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Scheduled parse task + */ +static struct GNUNET_SCHEDULER_Task *parse_task; + +/** + * The current state of the parser + */ +static int state; + +enum ZonefileImportState +{ + + /* Uninitialized */ + ZS_READY, + + /* The initial state */ + ZS_ORIGIN_SET, + + /* The $ORIGIN has changed */ + ZS_ORIGIN_CHANGED, + + /* The record name/label has changed */ + ZS_NAME_CHANGED + +}; + + + +/** + * Task run on shutdown. Cleans up everything. + * + * @param cls unused + */ +static void +do_shutdown (void *cls) +{ + (void) cls; + if (NULL != ego_name) + GNUNET_free (ego_name); + if (NULL != el) + { + GNUNET_IDENTITY_ego_lookup_cancel (el); + el = NULL; + } + if (NULL != ns_qe) + GNUNET_NAMESTORE_cancel (ns_qe); + if (NULL != id_op) + GNUNET_IDENTITY_cancel (id_op); + if (NULL != ns) + GNUNET_NAMESTORE_disconnect (ns); + if (NULL != id) + GNUNET_IDENTITY_disconnect (id); + for (int i = 0; i < rd_count; i++) + { + void *rd_ptr = (void*) rd[i].data; + GNUNET_free (rd_ptr); + } + if (NULL != parse_task) + GNUNET_SCHEDULER_cancel (parse_task); +} + +static void +tx_end (void *cls, enum GNUNET_ErrorCode ec) +{ + ns_qe = NULL; + if (GNUNET_EC_NONE != ec) + { + fprintf (stderr, + _ ("Ego `%s' not known to identity service\n"), + ego_name); + GNUNET_SCHEDULER_shutdown (); + ret = -1; + } + GNUNET_SCHEDULER_shutdown (); +} + +static void +parse (void *cls); + +static char* +trim (char *line) +{ + char *ltrimmed = line; + int ltrimmed_len; + int quoted = 0; + + // Trim all whitespace to the left + while (*ltrimmed == ' ') + ltrimmed++; + ltrimmed_len = strlen (ltrimmed); + // Find the first occurence of an unqoted ';', which is our comment + for (int i = 0; i < ltrimmed_len; i++) + { + if (ltrimmed[i] == '"') + quoted = ! quoted; + if ((ltrimmed[i] != ';') || quoted) + continue; + ltrimmed[i] = '\0'; + } + ltrimmed_len = strlen (ltrimmed); + // Remove trailing whitespace + for (int i = ltrimmed_len; i > 0; i--) + { + if (ltrimmed[i - 1] != ' ') + break; + ltrimmed[i - 1] = '\0'; + } + ltrimmed_len = strlen (ltrimmed); + if (ltrimmed[ltrimmed_len - 1] == '\n') + ltrimmed[ltrimmed_len - 1] = ' '; + return ltrimmed; +} + +static char* +next_token (char *token) +{ + char *next = token; + while (*next == ' ') + next++; + return next; +} + +static int +parse_ttl (char *token, struct GNUNET_TIME_Relative *ttl) +{ + char *next; + unsigned int ttl_tmp; + + next = strchr (token, ';'); + if (NULL != next) + next[0] = '\0'; + next = strchr (token, ' '); + if (NULL != next) + next[0] = '\0'; + if (1 != sscanf (token, "%u", &ttl_tmp)) + { + fprintf (stderr, "Unable to parse TTL `%s'\n", token); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TTL is: %u\n", ttl_tmp); + ttl->rel_value_us = ttl_tmp * 1000 * 1000; + return GNUNET_OK; +} + +static int +parse_origin (char *token, char *origin) +{ + char *next; + next = strchr (token, ';'); + if (NULL != next) + next[0] = '\0'; + next = strchr (token, ' '); + if (NULL != next) + next[0] = '\0'; + strcpy (origin, token); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin is: %s\n", origin); + return GNUNET_OK; +} + +static void +origin_create_cb (void *cls, const struct GNUNET_CRYPTO_PrivateKey *pk, + enum GNUNET_ErrorCode ec) +{ + id_op = NULL; + if (GNUNET_EC_NONE != ec) + { + fprintf (stderr, "Error: %s\n", GNUNET_ErrorCode_get_hint (ec)); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + state = ZS_ORIGIN_SET; + zone_pkey = *pk; + parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); +} + +static void +origin_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) +{ + + el = NULL; + + if (NULL == ego) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "$ORIGIN %s does not exist, creating...\n", ego_name); + id_op = GNUNET_IDENTITY_create (id, ego_name, NULL, + GNUNET_PUBLIC_KEY_TYPE_ECDSA, // FIXME make configurable + origin_create_cb, + NULL); + return; + } + state = ZS_ORIGIN_SET; + zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); + parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); +} + +static void +add_continuation (void *cls, enum GNUNET_ErrorCode ec) +{ + ns_qe = NULL; + if (GNUNET_EC_NONE != ec) + { + fprintf (stderr, + _ ("Failed to store records...\n")); + GNUNET_SCHEDULER_shutdown (); + ret = -1; + } + if (ZS_ORIGIN_CHANGED == state) + { + if (NULL != ego_name) + GNUNET_free (ego_name); + ego_name = GNUNET_strdup (origin); + if (ego_name[strlen (ego_name) - 1] == '.') + ego_name[strlen (ego_name) - 1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Changing origin to %s\n", ego_name); + el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, + &origin_lookup_cb, NULL); + return; + } + parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); +} + + + +/** + * Main function that will be run. + * + * TODO: + * - We must assume that names are not repeated later in the zonefile because + * our _store APIs are replacing. No sure if that is common in zonefiles. + * - We must only actually store a record set when the name to store changes or + * the end of the file is reached. + * that way we can group them and add (see above). + * - We need to hope our string formats are compatible, but seems ok. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +parse (void *cls) +{ + char buf[MAX_ZONEFILE_LINE_LEN]; + char payload[MAX_ZONEFILE_RECORD_DATA_LEN]; + char *next; + char *token; + char *payload_pos; + static char lastname[GNUNET_DNSPARSER_MAX_LABEL_LENGTH]; + char newname[GNUNET_DNSPARSER_MAX_LABEL_LENGTH]; + void *data; + size_t data_size; + int ttl_line = 0; + int type; + int bracket_unclosed = 0; + int quoted = 0; + + parse_task = NULL; + /* use filename provided as 1st argument (stdin by default) */ + int ln = 0; + while ((res = fgets (buf, sizeof(buf), stdin))) /* read each line of input */ + { + ln++; + ttl_line = 0; + token = trim (buf); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trimmed line (bracket %s): `%s'\n", + (bracket_unclosed > 0) ? "unclosed" : "closed", + token); + if ((0 == strlen (token)) || + ((1 == strlen (token)) && (' ' == *token))) + continue; // I guess we can safely ignore blank lines + if (bracket_unclosed == 0) + { + /* Payload is already parsed */ + payload_pos = payload; + /* Find space */ + next = strchr (token, ' '); + if (NULL == next) + { + fprintf (stderr, "Error at line %u: %s\n", ln, token); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + next[0] = '\0'; + next++; + if (0 == (strcmp (token, "$ORIGIN"))) + { + state = ZS_ORIGIN_CHANGED; + token = next_token (next); + } + else if (0 == (strcmp (token, "$TTL"))) + { + ttl_line = 1; + token = next_token (next); + } + else + { + if (0 == strcmp (token, "IN")) // Inherit name from before + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Old name: %s\n", lastname); + strcpy (newname, lastname); + token[strlen (token)] = ' '; + } + else if (token[strlen (token) - 1] != '.') // no fqdn + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: %s\n", token); + if (GNUNET_DNSPARSER_MAX_LABEL_LENGTH < strlen (token)) + { + fprintf (stderr, + _ ("Name `%s' is too long\n"), + token); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + strcpy (newname, token); + token = next_token (next); + } + else if (0 == strcmp (token, origin)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: @\n"); + strcpy (newname, "@"); + token = next_token (next); + } + else + { + if (strlen (token) < strlen (origin)) + { + fprintf (stderr, "Wrong origin: %s (expected %s)\n", token, origin); + break; // FIXME error? + } + if (0 != strcmp (token + (strlen (token) - strlen (origin)), origin)) + { + fprintf (stderr, "Wrong origin: %s (expected %s)\n", token, origin); + break; + } + token[strlen (token) - strlen (origin) - 1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: %s\n", token); + if (GNUNET_DNSPARSER_MAX_LABEL_LENGTH < strlen (token)) + { + fprintf (stderr, + _ ("Name `%s' is too long\n"), + token); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + strcpy (newname, token); + token = next_token (next); + } + if (0 != strcmp (newname, lastname) && + (0 < rd_count)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name changed %s->%s, storing record set of %u elements\n", + lastname, newname, + rd_count); + state = ZS_NAME_CHANGED; + } + else { + strcpy (lastname, newname); + } + } + + if (ttl_line) + { + if (GNUNET_SYSERR == parse_ttl (token, &ttl)) + { + fprintf (stderr, _ ("Failed to parse $TTL\n")); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + continue; + } + if (ZS_ORIGIN_CHANGED == state) + { + if (GNUNET_SYSERR == parse_origin (token, origin)) + { + fprintf (stderr, _ ("Failed to parse $ORIGIN from %s\n"), token); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + break; + } + if (ZS_READY == state) + { + fprintf (stderr, + _ ( + "You must provide $ORIGIN in your zonefile or via arguments (--zone)!\n")); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + // This is a record, let's go + if (MAX_RECORDS_PER_NAME == rd_count) + { + fprintf (stderr, + _ ("Only %u records per unique name supported.\n"), + MAX_RECORDS_PER_NAME); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + rd[rd_count].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + rd[rd_count].expiration_time = ttl.rel_value_us; + next = strchr (token, ' '); + if (NULL == next) + { + fprintf (stderr, "Error, last token: %s\n", token); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + break; + } + next[0] = '\0'; + next++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "class is: %s\n", token); + while (*next == ' ') + next++; + token = next; + next = strchr (token, ' '); + if (NULL == next) + { + fprintf (stderr, "Error\n"); + break; + } + next[0] = '\0'; + next++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "type is: %s\n", token); + type = GNUNET_GNSRECORD_typename_to_number (token); + rd[rd_count].record_type = type; + while (*next == ' ') + next++; + token = next; + } + for (int i = 0; i < strlen (token); i++) + { + if (token[i] == '"') + quoted = ! quoted; + if ((token[i] == '(') && ! quoted) + bracket_unclosed++; + if ((token[i] == ')') && ! quoted) + bracket_unclosed--; + } + memcpy (payload_pos, token, strlen (token)); + payload_pos += strlen (token); + if (bracket_unclosed > 0) + { + *payload_pos = ' '; + payload_pos++; + continue; + } + *payload_pos = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "data is: %s\n\n", payload); + if (GNUNET_OK != + GNUNET_GNSRECORD_string_to_value (type, payload, + &data, + &data_size)) + { + fprintf (stderr, + _ ("Data `%s' invalid\n"), + payload); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + rd[rd_count].data = data; + rd[rd_count].data_size = data_size; + if (ZS_NAME_CHANGED == state) + break; + rd_count++; + } + if (rd_count > 0) + { + ns_qe = GNUNET_NAMESTORE_records_store (ns, + &zone_pkey, + lastname, + rd_count, + rd, + &add_continuation, + NULL); + published_sets++; + published_records += rd_count; + for (int i = 0; i < rd_count; i++) + { + data = (void*) rd[i].data; + GNUNET_free (data); + } + if (ZS_NAME_CHANGED == state) + { + rd[0] = rd[rd_count]; // recover last rd parsed. + rd_count = 1; + strcpy (lastname, newname); + state = ZS_ORIGIN_SET; + } + else + rd_count = 0; + return; + } + if (ZS_ORIGIN_CHANGED == state) + { + if (NULL != ego_name) + GNUNET_free (ego_name); + ego_name = GNUNET_strdup (origin); + if (ego_name[strlen (ego_name) - 1] == '.') + ego_name[strlen (ego_name) - 1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Changing origin to %s\n", ego_name); + el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, + &origin_lookup_cb, NULL); + return; + } + printf ("Published %u records sets with total %u records\n", + published_sets, published_records); + ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, + &tx_end, + NULL); +} + +static void +tx_start (void *cls, enum GNUNET_ErrorCode ec) +{ + ns_qe = NULL; + if (GNUNET_EC_NONE != ec) + { + fprintf (stderr, + _ ("Ego `%s' not known to identity service\n"), + ego_name); + GNUNET_SCHEDULER_shutdown (); + ret = -1; + return; + } + parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); +} + +static void +identity_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) +{ + + el = NULL; + if (NULL == ego) + { + if (NULL != ego_name) + { + fprintf (stderr, + _ ("Ego `%s' not known to identity service\n"), + ego_name); + + } + GNUNET_SCHEDULER_shutdown (); + ret = -1; + return; + } + zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); + sprintf (origin, "%s.", ego_name); + state = ZS_ORIGIN_SET; + ns_qe = GNUNET_NAMESTORE_transaction_begin (ns, + &tx_start, + NULL); +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *_cfg) +{ + cfg = _cfg; + ns = GNUNET_NAMESTORE_connect (cfg); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg); + if (NULL == ns) + { + fprintf (stderr, + _ ("Failed to connect to NAMESTORE\n")); + return; + } + id = GNUNET_IDENTITY_connect (cfg, NULL, NULL); + if (NULL == id) + { + fprintf (stderr, + _ ("Failed to connect to IDENTITY\n")); + return; + } + if (NULL != ego_name) + el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &identity_cb, (void *) cfg); + else + parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); + state = ZS_READY; +} + + +/** + * The main function for gnunet-namestore-dbtool. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('z', + "zone", + "EGO", + gettext_noop ( + "name of the ego controlling the zone"), + &ego_name), + GNUNET_GETOPT_OPTION_END + }; + int lret; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + GNUNET_log_setup ("gnunet-namestore-dbtool", "WARNING", NULL); + if (GNUNET_OK != + (lret = GNUNET_PROGRAM_run (argc, + argv, + "gnunet-namestore-zonefile", + _ ( + "GNUnet namestore database manipulation tool"), + options, + &run, + NULL))) + { + GNUNET_free_nz ((void *) argv); + return lret; + } + GNUNET_free_nz ((void *) argv); + return ret; +} diff --git a/src/cli/namestore/gnunet-namestore.c b/src/cli/namestore/gnunet-namestore.c new file mode 100644 index 000000000..baa036ac7 --- /dev/null +++ b/src/cli/namestore/gnunet-namestore.c @@ -0,0 +1,2120 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012, 2013, 2014, 2019 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file gnunet-namestore.c + * @brief command line tool to manipulate the local zone + * @author Christian Grothoff + * + * TODO: + * - test + */ +#include "platform.h" +#include +#include +#include +#include +#include +#include + +/** + * The upper bound for the zone iteration interval + * (per record). + */ +#define WARN_RELATIVE_EXPIRATION_LIMIT GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_MINUTES, 15) + +/** + * Entry in record set for bulk processing. + */ +struct RecordSetEntry +{ + /** + * Kept in a linked list. + */ + struct RecordSetEntry *next; + + /** + * The record to add/remove. + */ + struct GNUNET_GNSRECORD_Data record; +}; + +/** + * The record marked for deletion + */ +struct MarkedRecord +{ + /** + * DLL + */ + struct MarkedRecord *next; + + /** + * DLL + */ + struct MarkedRecord *prev; + + /** + * Ego Identifier + */ + char *name; + + /** + * The zone key + */ + struct GNUNET_CRYPTO_PrivateKey key; +}; + +/** + * The default namestore ego + */ +struct EgoEntry +{ + /** + * DLL + */ + struct EgoEntry *next; + + /** + * DLL + */ + struct EgoEntry *prev; + + /** + * Ego Identifier + */ + char *identifier; + + /** + * The Ego + */ + struct GNUNET_IDENTITY_Ego *ego; +}; + +/** + * Handle to the namestore. + */ +static struct GNUNET_NAMESTORE_Handle *ns; + +/** + * Private key for the our zone. + */ +static struct GNUNET_CRYPTO_PrivateKey zone_pkey; + +/** + * Identity service handle + */ +static struct GNUNET_IDENTITY_Handle *idh; + +/** + * Name of the ego controlling the zone. + */ +static char *ego_name; + +/** + * Queue entry for the 'add-uri' operation. + */ +static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri; + +/** + * Queue entry for the 'add' operation. + */ +static struct GNUNET_NAMESTORE_QueueEntry *add_qe; + +/** + * Queue entry for the 'lookup' operation. + */ +static struct GNUNET_NAMESTORE_QueueEntry *get_qe; + +/** + * Queue entry for the 'reverse lookup' operation (in combination with a name). + */ +static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe; + +/** + * Marked record list + */ +static struct MarkedRecord *marked_head; + +/** + * Marked record list + */ +static struct MarkedRecord *marked_tail; + +/** + * Configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Ego list + */ +static struct EgoEntry *ego_head; + +/** + * Ego list + */ +static struct EgoEntry *ego_tail; + +/** + * List iterator for the 'list' operation. + */ +static struct GNUNET_NAMESTORE_ZoneIterator *list_it; + +/** + * Run in read from stdin mode. + */ +static int read_from_stdin; + +/** + * Desired action is to list records. + */ +static int list; + +/** + * Desired action is to add a record. + */ +static int add; + +/** + * Desired action is to remove a record. + */ +static int del; + +/** + * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE) + */ +static int is_public; + +/** + * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW) + */ +static int is_shadow; + +/** + * Filter private records + */ +static int omit_private; + +/** + * Output in recordline format + */ +static int output_recordline; + + +/** + * Purge zone contents + */ +static int purge_zone; + +/** + * Do not filter maintenance records + */ +static int include_maintenance; + +/** + * Purge orphaned records + */ +static int purge_orphaned; + +/** + * List records and zone keys of orphaned records + */ +static int list_orphaned; + +/** + * Queue entry for the 'del' operation. + */ +static struct GNUNET_NAMESTORE_QueueEntry *del_qe; + +/** + * Queue entry for the 'set/replace' operation. + */ +static struct GNUNET_NAMESTORE_QueueEntry *set_qe; + +/** + * Queue entry for begin/commit + */ +static struct GNUNET_NAMESTORE_QueueEntry *ns_qe; + +/** + * Name of the records to add/list/remove. + */ +static char *name; + +/** + * Value of the record to add/remove. + */ +static char *value; + +/** + * URI to import. + */ +static char *uri; + +/** + * Reverse lookup to perform. + */ +static char *reverse_pkey; + +/** + * Type of the record to add/remove, NULL to remove all. + */ +static char *typestring; + +/** + * Desired expiration time. + */ +static char *expirationstring; + +/** + * Desired nick name. + */ +static char *nickstring; + +/** + * Global return value + */ +static int ret; + +/** + * Type string converted to DNS type value. + */ +static uint32_t type; + +/** + * Value in binary format. + */ +static void *data; + +/** + * Number of bytes in #data. + */ +static size_t data_size; + +/** + * Expiration string converted to numeric value. + */ +static uint64_t etime; + +/** + * Is expiration time relative or absolute time? + */ +static int etime_is_rel = GNUNET_SYSERR; + +/** + * Monitor handle. + */ +static struct GNUNET_NAMESTORE_ZoneMonitor *zm; + +/** + * Enables monitor mode. + */ +static int monitor; + +/** + * Entry in record set for processing records in bulk. + */ +static struct RecordSetEntry *recordset; + +/** + * Purge task + */ +static struct GNUNET_SCHEDULER_Task *purge_task; + +/** + * Parse expiration time. + * + * @param expirationstring text to parse + * @param[out] etime_is_rel set to #GNUNET_YES if time is relative + * @param[out] etime set to expiration time (abs or rel) + * @return #GNUNET_OK on success + */ +static int +parse_expiration (const char *expirationstring, + int *etime_is_rel, + uint64_t *etime) +{ + struct GNUNET_TIME_Relative etime_rel; + struct GNUNET_TIME_Absolute etime_abs; + + if (0 == strcmp (expirationstring, "never")) + { + *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; + *etime_is_rel = GNUNET_NO; + return GNUNET_OK; + } + if (GNUNET_OK == + GNUNET_STRINGS_fancy_time_to_relative (expirationstring, &etime_rel)) + { + *etime_is_rel = GNUNET_YES; + *etime = etime_rel.rel_value_us; + if (GNUNET_TIME_relative_cmp (etime_rel, <, WARN_RELATIVE_EXPIRATION_LIMIT)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Relative expiration times of less than %s are not recommended. To improve availability, consider increasing this value.\n", + GNUNET_STRINGS_relative_time_to_string ( + WARN_RELATIVE_EXPIRATION_LIMIT, GNUNET_NO)); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing record with relative expiration time of %s\n", + GNUNET_STRINGS_relative_time_to_string (etime_rel, GNUNET_NO)); + return GNUNET_OK; + } + if (GNUNET_OK == + GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, &etime_abs)) + { + *etime_is_rel = GNUNET_NO; + *etime = etime_abs.abs_value_us; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing record with absolute expiration time of %s\n", + GNUNET_STRINGS_absolute_time_to_string (etime_abs)); + return GNUNET_OK; + } + return GNUNET_SYSERR; +} + + +static int +parse_recordline (const char *line) +{ + struct RecordSetEntry **head = &recordset; + struct RecordSetEntry *r; + struct GNUNET_GNSRECORD_Data record; + char *cp; + char *tok; + char *saveptr; + void *raw_data; + + cp = GNUNET_strdup (line); + tok = strtok_r (cp, " ", &saveptr); + if (NULL == tok) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Missing entries in record line `%s'.\n"), + line); + GNUNET_free (cp); + return GNUNET_SYSERR; + } + record.record_type = GNUNET_GNSRECORD_typename_to_number (tok); + if (UINT32_MAX == record.record_type) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Unknown record type `%s'\n"), tok); + GNUNET_free (cp); + return GNUNET_SYSERR; + } + tok = strtok_r (NULL, " ", &saveptr); + if (NULL == tok) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Empty record line argument is not allowed.\n")); + GNUNET_free (cp); + return GNUNET_SYSERR; + } + if (1 != sscanf (tok, "%" SCNu64, &record.expiration_time)) + { + fprintf (stderr, + _ ("Error parsing expiration time %s.\n"), tok); + GNUNET_free (cp); + return GNUNET_SYSERR; + } + tok = strtok_r (NULL, " ", &saveptr); + if (NULL == tok) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Empty record line argument is not allowed.\n")); + GNUNET_free (cp); + return GNUNET_SYSERR; + } + record.flags = GNUNET_GNSRECORD_RF_NONE; + if (NULL != strchr (tok, (unsigned char) 'r')) + record.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + if (NULL == strchr (tok, (unsigned char) 'p')) /* p = public */ + record.flags |= GNUNET_GNSRECORD_RF_PRIVATE; + if (NULL != strchr (tok, (unsigned char) 'S')) + record.flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL; + if (NULL != strchr (tok, (unsigned char) 's')) + record.flags |= GNUNET_GNSRECORD_RF_SHADOW; + if (NULL != strchr (tok, (unsigned char) 'C')) + record.flags |= GNUNET_GNSRECORD_RF_CRITICAL; + tok += strlen (tok) + 1; + if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (record.record_type, + tok, + &raw_data, + &record.data_size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Invalid record data for type %s: `%s'.\n"), + GNUNET_GNSRECORD_number_to_typename (record.record_type), + tok); + GNUNET_free (cp); + return GNUNET_SYSERR; + } + GNUNET_free (cp); + + r = GNUNET_malloc (sizeof(struct RecordSetEntry) + record.data_size); + r->next = *head; + record.data = &r[1]; + memcpy (&r[1], raw_data, record.data_size); + GNUNET_free (raw_data); + r->record = record; + *head = r; + return GNUNET_OK; +} + +static void +reset_handles (void) +{ + struct MarkedRecord *mrec; + struct MarkedRecord *mrec_tmp; + struct RecordSetEntry *rs_entry; + + rs_entry = recordset; + while (NULL != (rs_entry = recordset)) + { + recordset = recordset->next; + GNUNET_free (rs_entry); + } + recordset = NULL; + if (NULL != ego_name) + { + GNUNET_free (ego_name); + ego_name = NULL; + } + if (NULL != name) + { + GNUNET_free (name); + name = NULL; + } + if (NULL != value) + { + GNUNET_free (value); + value = NULL; + } + if (NULL != uri) + { + GNUNET_free (uri); + uri = NULL; + } + if (NULL != expirationstring) + { + GNUNET_free (expirationstring); + expirationstring = NULL; + } + if (NULL != purge_task) + { + GNUNET_SCHEDULER_cancel (purge_task); + purge_task = NULL; + } + for (mrec = marked_head; NULL != mrec;) + { + mrec_tmp = mrec; + mrec = mrec->next; + GNUNET_free (mrec_tmp->name); + GNUNET_free (mrec_tmp); + } + if (NULL != list_it) + { + GNUNET_NAMESTORE_zone_iteration_stop (list_it); + list_it = NULL; + } + if (NULL != add_qe) + { + GNUNET_NAMESTORE_cancel (add_qe); + add_qe = NULL; + } + if (NULL != set_qe) + { + GNUNET_NAMESTORE_cancel (set_qe); + set_qe = NULL; + } + if (NULL != add_qe_uri) + { + GNUNET_NAMESTORE_cancel (add_qe_uri); + add_qe_uri = NULL; + } + if (NULL != get_qe) + { + GNUNET_NAMESTORE_cancel (get_qe); + get_qe = NULL; + } + if (NULL != del_qe) + { + GNUNET_NAMESTORE_cancel (del_qe); + del_qe = NULL; + } + if (NULL != reverse_qe) + { + GNUNET_NAMESTORE_cancel (reverse_qe); + reverse_qe = NULL; + } + memset (&zone_pkey, 0, sizeof(zone_pkey)); + if (NULL != zm) + { + GNUNET_NAMESTORE_zone_monitor_stop (zm); + zm = NULL; + } + if (NULL != data) + { + GNUNET_free (data); + data = NULL; + } + if (NULL != typestring) + { + GNUNET_free (typestring); + typestring = NULL; + } + list = 0; + is_public = 0; + is_shadow = 0; + purge_zone = 0; +} + + + +/** + * Task run on shutdown. Cleans up everything. + * + * @param cls unused + */ +static void +do_shutdown (void *cls) +{ + struct EgoEntry *ego_entry; + struct EgoEntry *ego_tmp; + (void) cls; + + reset_handles (); + if (NULL != ns_qe) + { + GNUNET_NAMESTORE_cancel (ns_qe); + ns_qe = NULL; + } + if (NULL != ns) + { + GNUNET_NAMESTORE_disconnect (ns); + ns = NULL; + } + if (NULL != idh) + { + GNUNET_IDENTITY_disconnect (idh); + idh = NULL; + } + for (ego_entry = ego_head; NULL != ego_entry;) + { + ego_tmp = ego_entry; + ego_entry = ego_entry->next; + GNUNET_free (ego_tmp->identifier); + GNUNET_free (ego_tmp); + } +} + +static void +commit_cb (void *cls, enum GNUNET_ErrorCode ec) +{ + ns_qe = NULL; + if (GNUNET_EC_NONE != ec) + { + fprintf (stderr, "Failed to commit to namestore: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + ret = 1; + } + GNUNET_SCHEDULER_shutdown (); +} + +static void +process_command_stdin (); + + +static void +finish_command (void) +{ + reset_handles (); + if (read_from_stdin) + { + process_command_stdin (); + return; + } + ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, &commit_cb, NULL); +} + + +static void +add_continuation (void *cls, enum GNUNET_ErrorCode ec) +{ + struct GNUNET_NAMESTORE_QueueEntry **qe = cls; + + *qe = NULL; + if (GNUNET_EC_NONE != ec) + { + fprintf (stderr, + _ ("Adding record failed: %s\n"), + GNUNET_ErrorCode_get_hint (ec)); + if (GNUNET_EC_NAMESTORE_RECORD_EXISTS != ec) + ret = 1; + } + ret = 0; + finish_command (); +} + + +static void +del_continuation (void *cls, enum GNUNET_ErrorCode ec) +{ + (void) cls; + del_qe = NULL; + if (GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND == ec) + { + fprintf (stderr, + _ ("Deleting record failed: %s\n"), GNUNET_ErrorCode_get_hint ( + ec)); + } + finish_command (); +} + +static void +purge_next_record (void *cls); + +static void +marked_deleted (void *cls, enum GNUNET_ErrorCode ec) +{ + del_qe = NULL; + if (GNUNET_EC_NONE != ec) + { + fprintf (stderr, + _ ("Deleting record failed: %s\n"), + GNUNET_ErrorCode_get_hint (ec)); + } + purge_task = GNUNET_SCHEDULER_add_now (&purge_next_record, NULL); +} + + +static void +purge_next_record (void *cls) +{ + struct MarkedRecord *mrec; + purge_task = NULL; + + if (NULL == marked_head) + { + ret = 0; + finish_command (); + return; + } + mrec = marked_head; + GNUNET_CONTAINER_DLL_remove (marked_head, + marked_tail, + mrec); + del_qe = GNUNET_NAMESTORE_records_store (ns, + &mrec->key, + mrec->name, + 0, NULL, + &marked_deleted, + NULL); + GNUNET_free (mrec->name); + GNUNET_free (mrec); +} + +/** + * Function called when we are done with a zone iteration. + */ +static void +zone_iteration_finished (void *cls) +{ + (void) cls; + list_it = NULL; + if (purge_orphaned || purge_zone) + { + purge_task = GNUNET_SCHEDULER_add_now (&purge_next_record, NULL); + return; + } + ret = 0; + finish_command (); +} + + +/** + * Function called when we encountered an error in a zone iteration. + */ +static void +zone_iteration_error_cb (void *cls) +{ + (void) cls; + list_it = NULL; + fprintf (stderr, "Error iterating over zone\n"); + ret = 1; + finish_command (); +} + +static void +collect_zone_records_to_purge (const struct + GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct MarkedRecord *mrec; + + mrec = GNUNET_new (struct MarkedRecord); + mrec->key = *zone_key; + mrec->name = GNUNET_strdup (rname); + GNUNET_CONTAINER_DLL_insert (marked_head, + marked_tail, + mrec); +} + + +static void +collect_orphans (const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct EgoEntry *ego; + struct MarkedRecord *orphan; + int is_orphaned = 1; + + for (ego = ego_head; NULL != ego; ego = ego->next) + { + if (0 == memcmp (GNUNET_IDENTITY_ego_get_private_key (ego->ego), + zone_key, + sizeof (*zone_key))) + { + is_orphaned = 0; + break; + } + } + if (is_orphaned) + { + orphan = GNUNET_new (struct MarkedRecord); + orphan->key = *zone_key; + orphan->name = GNUNET_strdup (rname); + GNUNET_CONTAINER_DLL_insert (marked_head, + marked_tail, + orphan); + } +} + +/** + * Process a record that was stored in the namestore. + * + * @param rname name that is being mapped (at most 255 characters long) + * @param rd_len number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +display_record (const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd) +{ + const char *typestr; + char *s; + const char *ets; + struct GNUNET_TIME_Absolute at; + struct GNUNET_TIME_Relative rt; + struct EgoEntry *ego; + int have_record; + int is_orphaned = 1; + char *orphaned_str; + + if ((NULL != name) && (0 != strcmp (name, rname))) + return; + have_record = GNUNET_NO; + for (unsigned int i = 0; i < rd_len; i++) + { + if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) && + (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT))) + continue; + if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type)) + continue; + have_record = GNUNET_YES; + break; + } + if (GNUNET_NO == have_record) + return; + for (ego = ego_head; NULL != ego; ego = ego->next) + { + if (0 == memcmp (GNUNET_IDENTITY_ego_get_private_key (ego->ego), + zone_key, + sizeof (*zone_key))) + { + is_orphaned = 0; + break; + } + } + if (list_orphaned && ! is_orphaned) + return; + if (! list_orphaned && is_orphaned) + return; + orphaned_str = GNUNET_CRYPTO_private_key_to_string (zone_key); + fprintf (stdout, "%s.%s:\n", rname, is_orphaned ? orphaned_str : + ego->identifier); + GNUNET_free (orphaned_str); + if (NULL != typestring) + type = GNUNET_GNSRECORD_typename_to_number (typestring); + else + type = GNUNET_GNSRECORD_TYPE_ANY; + for (unsigned int i = 0; i < rd_len; i++) + { + if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) && + (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT))) + continue; + if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type)) + continue; + typestr = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type); + s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type, + rd[i].data, + rd[i].data_size); + if (NULL == s) + { + fprintf (stdout, + _ ("\tCorrupt or unsupported record of type %u\n"), + (unsigned int) rd[i].record_type); + continue; + } + if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) + { + rt.rel_value_us = rd[i].expiration_time; + ets = GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_YES); + } + else + { + at.abs_value_us = rd[i].expiration_time; + ets = GNUNET_STRINGS_absolute_time_to_string (at); + } + char flgstr[16]; + sprintf (flgstr, "[%s%s%s%s%s]", + (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE) ? "" : "p", + (rd[i].flags & GNUNET_GNSRECORD_RF_SUPPLEMENTAL) ? "S" : "", + (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION) ? "r" : "", + (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW) ? "S" : "", + (rd[i].flags & GNUNET_GNSRECORD_RF_CRITICAL) ? "C" : ""); + if (output_recordline) + fprintf (stdout, + " %s %" PRIu64 " %s %s\n", + typestr, + rd[i].expiration_time, + flgstr, + s); + else + fprintf (stdout, + "\t%s: %s (%s)\t%s\t%s\n", + typestr, + s, + ets, + (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE)) ? "PRIVATE" + : "PUBLIC", + (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW)) ? "SHADOW" + : ""); + GNUNET_free (s); + } + // fprintf (stdout, "%s", "\n"); +} + +static void +purge_zone_iterator (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd, + struct GNUNET_TIME_Absolute expiry) +{ + (void) cls; + (void) zone_key; + (void) expiry; + collect_zone_records_to_purge (zone_key, rname, rd_len, rd); + GNUNET_NAMESTORE_zone_iterator_next (list_it, 1); +} + + +static void +purge_orphans_iterator (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd, + struct GNUNET_TIME_Absolute expiry) +{ + (void) cls; + (void) zone_key; + (void) expiry; + collect_orphans (zone_key, rname, rd_len, rd); + GNUNET_NAMESTORE_zone_iterator_next (list_it, 1); +} + + +/** + * Process a record that was stored in the namestore. + * + * @param cls closure + * @param zone_key private key of the zone + * @param rname name that is being mapped (at most 255 characters long) + * @param rd_len number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +display_record_iterator (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd, + struct GNUNET_TIME_Absolute expiry) +{ + (void) cls; + (void) zone_key; + (void) expiry; + display_record (zone_key, rname, rd_len, rd); + GNUNET_NAMESTORE_zone_iterator_next (list_it, 1); +} + + +/** + * Process a record that was stored in the namestore. + * + * @param cls closure + * @param zone_key private key of the zone + * @param rname name that is being mapped (at most 255 characters long) + * @param rd_len number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +display_record_monitor (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd, + struct GNUNET_TIME_Absolute expiry) +{ + (void) cls; + (void) zone_key; + (void) expiry; + display_record (zone_key, rname, rd_len, rd); + GNUNET_NAMESTORE_zone_monitor_next (zm, 1); +} + + +/** + * Process a record that was stored in the namestore. + * + * @param cls closure + * @param zone_key private key of the zone + * @param rname name that is being mapped (at most 255 characters long) + * @param rd_len number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +display_record_lookup (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd) +{ + (void) cls; + (void) zone_key; + get_qe = NULL; + display_record (zone_key, rname, rd_len, rd); + finish_command (); +} + + +/** + * Function called once we are in sync in monitor mode. + * + * @param cls NULL + */ +static void +sync_cb (void *cls) +{ + (void) cls; + fprintf (stdout, "%s", "Monitor is now in sync.\n"); +} + + +/** + * Function called on errors while monitoring. + * + * @param cls NULL + */ +static void +monitor_error_cb (void *cls) +{ + (void) cls; + fprintf (stderr, "%s", "Monitor disconnected and out of sync.\n"); +} + + +/** + * Function called on errors while monitoring. + * + * @param cls NULL + */ +static void +lookup_error_cb (void *cls) +{ + (void) cls; + get_qe = NULL; + fprintf (stderr, "%s", "Failed to lookup record.\n"); + finish_command (); +} + + +/** + * Function called if lookup fails. + */ +static void +add_error_cb (void *cls) +{ + (void) cls; + add_qe = NULL; + GNUNET_break (0); + ret = 1; + finish_command (); +} + + +/** + * We're storing a record; this function is given the existing record + * so that we can merge the information. + * + * @param cls closure, unused + * @param zone_key private key of the zone + * @param rec_name name that is being mapped (at most 255 characters long) + * @param rd_count number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +get_existing_record (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rec_name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct GNUNET_GNSRECORD_Data rdn[rd_count + 1]; + struct GNUNET_GNSRECORD_Data *rde; + + (void) cls; + (void) zone_key; + add_qe = NULL; + if (0 != strcmp (rec_name, name)) + { + GNUNET_break (0); + ret = 1; + finish_command (); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u records for name `%s'\n", + rd_count, + rec_name); + for (unsigned int i = 0; i < rd_count; i++) + { + switch (rd[i].record_type) + { + case GNUNET_DNSPARSER_TYPE_SOA: + if (GNUNET_DNSPARSER_TYPE_SOA == type) + { + fprintf ( + stderr, + _ ( + "A SOA record exists already under `%s', cannot add a second SOA to the same zone.\n"), + rec_name); + ret = 1; + finish_command (); + return; + } + break; + } + } + memset (rdn, 0, sizeof(struct GNUNET_GNSRECORD_Data)); + GNUNET_memcpy (&rdn[1], rd, rd_count * sizeof(struct GNUNET_GNSRECORD_Data)); + rde = &rdn[0]; + rde->data = data; + rde->data_size = data_size; + rde->record_type = type; + if (1 == is_shadow) + rde->flags |= GNUNET_GNSRECORD_RF_SHADOW; + if (1 != is_public) + rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE; + rde->expiration_time = etime; + if (GNUNET_YES == etime_is_rel) + rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + else if (GNUNET_NO != etime_is_rel) + rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; + GNUNET_assert (NULL != name); + add_qe = GNUNET_NAMESTORE_records_store (ns, + &zone_pkey, + name, + rd_count + 1, + rde, + &add_continuation, + &add_qe); +} + + +/** + * Function called if we encountered an error in zone-to-name. + */ +static void +reverse_error_cb (void *cls) +{ + (void) cls; + reverse_qe = NULL; + fprintf (stdout, "%s.zkey\n", reverse_pkey); +} + + +/** + * Function called with the result of our attempt to obtain a name for a given + * public key. + * + * @param cls NULL + * @param zone private key of the zone; NULL on disconnect + * @param label label of the records; NULL on disconnect + * @param rd_count number of entries in @a rd array, 0 if label was deleted + * @param rd array of records with data to store + */ +static void +handle_reverse_lookup (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + (void) cls; + (void) zone; + (void) rd_count; + (void) rd; + reverse_qe = NULL; + if (NULL == label) + fprintf (stdout, "%s\n", reverse_pkey); + else + fprintf (stdout, "%s.%s\n", label, ego_name); + finish_command (); +} + + +/** + * Function called if lookup for deletion fails. + */ +static void +del_lookup_error_cb (void *cls) +{ + (void) cls; + del_qe = NULL; + GNUNET_break (0); + ret = 1; + finish_command (); +} + + +/** + * We were asked to delete something; this function is called with + * the existing records. Now we should determine what should be + * deleted and then issue the deletion operation. + * + * @param cls NULL + * @param zone private key of the zone we are deleting from + * @param label name of the records we are editing + * @param rd_count size of the @a rd array + * @param rd existing records + */ +static void +del_monitor (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct GNUNET_GNSRECORD_Data rdx[rd_count]; + unsigned int rd_left; + uint32_t type; + char *vs; + + (void) cls; + (void) zone; + del_qe = NULL; + if (0 == rd_count) + { + fprintf (stderr, + _ ( + "There are no records under label `%s' that could be deleted.\n"), + label); + ret = 1; + finish_command (); + return; + } + if ((NULL == value) && (NULL == typestring)) + { + /* delete everything */ + del_qe = GNUNET_NAMESTORE_records_store (ns, + &zone_pkey, + name, + 0, + NULL, + &del_continuation, + NULL); + return; + } + rd_left = 0; + if (NULL != typestring) + type = GNUNET_GNSRECORD_typename_to_number (typestring); + else + type = GNUNET_GNSRECORD_TYPE_ANY; + for (unsigned int i = 0; i < rd_count; i++) + { + vs = NULL; + if (! (((GNUNET_GNSRECORD_TYPE_ANY == type) || + (rd[i].record_type == type)) && + ((NULL == value) || + (NULL == + (vs = (GNUNET_GNSRECORD_value_to_string (rd[i].record_type, + rd[i].data, + rd[i].data_size)))) || + (0 == strcmp (vs, value))))) + rdx[rd_left++] = rd[i]; + GNUNET_free (vs); + } + if (rd_count == rd_left) + { + /* nothing got deleted */ + fprintf ( + stderr, + _ ( + "There are no records under label `%s' that match the request for deletion.\n"), + label); + finish_command (); + return; + } + /* delete everything but what we copied to 'rdx' */ + del_qe = GNUNET_NAMESTORE_records_store (ns, + &zone_pkey, + name, + rd_left, + rdx, + &del_continuation, + NULL); +} + + +static void +replace_cont (void *cls, enum GNUNET_ErrorCode ec) +{ + (void) cls; + + set_qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + _ ("%s\n"), + GNUNET_ErrorCode_get_hint (ec)); + ret = 1; /* fail from 'main' */ + } + finish_command (); +} + + +/** + * We have obtained the zone's private key, so now process + * the main commands using it. + * + * @param cfg configuration to use + */ +static void +run_with_zone_pkey (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_GNSRECORD_Data rd; + enum GNUNET_GNSRECORD_Filter filter_flags = GNUNET_GNSRECORD_FILTER_NONE; + + if (omit_private) + filter_flags |= GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE; + if (include_maintenance) + filter_flags |= GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE; + if (! (add | del | list | (NULL != nickstring) | (NULL != uri) + | (NULL != reverse_pkey) | (NULL != recordset) | (monitor) + | (purge_orphaned) | (list_orphaned) | (purge_zone)) ) + { + /* nothing more to be done */ + fprintf (stderr, _ ("No options given\n")); + finish_command (); + return; + } + + if (NULL != recordset) + { + /* replace entire record set */ + unsigned int rd_count; + struct GNUNET_GNSRECORD_Data *rd; + + /* FIXME: We could easily support append and delete with this as well */ + if (! add) + { + fprintf (stderr, _ ("Recordlines only work with option `%s'\n"), + "-a"); + ret = 1; + finish_command (); + return; + } + if (NULL == name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-n", + _ ("name")); + ret = 1; + finish_command (); + return; + } + rd_count = 0; + for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next) + rd_count++; + rd = GNUNET_new_array (rd_count, struct GNUNET_GNSRECORD_Data); + rd_count = 0; + for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next) + { + rd[rd_count] = e->record; + rd_count++; + } + set_qe = GNUNET_NAMESTORE_records_store (ns, + &zone_pkey, + name, + rd_count, + rd, + &replace_cont, + NULL); + GNUNET_free (rd); + return; + } + if (NULL != nickstring) + { + if (0 == strlen (nickstring)) + { + fprintf (stderr, _ ("Invalid nick `%s'\n"), nickstring); + ret = 1; + finish_command (); + return; + } + add = 1; + typestring = GNUNET_strdup (GNUNET_GNSRECORD_number_to_typename ( + GNUNET_GNSRECORD_TYPE_NICK)); + name = GNUNET_strdup (GNUNET_GNS_EMPTY_LABEL_AT); + value = GNUNET_strdup (nickstring); + is_public = 0; + expirationstring = GNUNET_strdup ("never"); + GNUNET_free (nickstring); + nickstring = NULL; + } + + if (add) + { + if (NULL == ego_name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-z", + _ ("add")); + ret = 1; + finish_command (); + return; + } + if (NULL == name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-n", + _ ("add")); + ret = 1; + finish_command (); + return; + } + if (NULL == typestring) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-t", + _ ("add")); + ret = 1; + finish_command (); + return; + } + type = GNUNET_GNSRECORD_typename_to_number (typestring); + if (UINT32_MAX == type) + { + fprintf (stderr, _ ("Unsupported type `%s'\n"), typestring); + ret = 1; + finish_command (); + return; + } + if ((GNUNET_DNSPARSER_TYPE_SRV == type) || + (GNUNET_DNSPARSER_TYPE_TLSA == type) || + (GNUNET_DNSPARSER_TYPE_OPENPGPKEY == type)) + { + fprintf (stderr, + _ ("For DNS record types `SRV', `TLSA' and `OPENPGPKEY'")); + fprintf (stderr, ", please use a `BOX' record instead\n"); + ret = 1; + finish_command (); + return; + } + if (NULL == value) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-V", + _ ("add")); + ret = 1; + finish_command (); + return; + } + if (GNUNET_OK != + GNUNET_GNSRECORD_string_to_value (type, value, &data, &data_size)) + { + fprintf (stderr, + _ ("Value `%s' invalid for record type `%s'\n"), + value, + typestring); + ret = 1; + finish_command (); + return; + } + if (NULL == expirationstring) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-e", + _ ("add")); + ret = 1; + finish_command (); + return; + } + if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime)) + { + fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring); + ret = 1; + finish_command (); + return; + } + add_qe = GNUNET_NAMESTORE_records_lookup (ns, + &zone_pkey, + name, + &add_error_cb, + NULL, + &get_existing_record, + NULL); + } + if (del) + { + if (NULL == ego_name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-z", + _ ("del")); + ret = 1; + finish_command (); + return; + } + if (NULL == name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-n", + _ ("del")); + ret = 1; + finish_command (); + return; + } + del_qe = GNUNET_NAMESTORE_records_lookup (ns, + &zone_pkey, + name, + &del_lookup_error_cb, + NULL, + &del_monitor, + NULL); + } + if (purge_orphaned) + { + list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns, + NULL, + &zone_iteration_error_cb, + NULL, + &purge_orphans_iterator, + NULL, + &zone_iteration_finished, + NULL, + filter_flags); + + } + else if (purge_zone) + { + if (NULL == ego_name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-z", + _ ("purge-zone")); + ret = 1; + finish_command (); + return; + } + list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns, + &zone_pkey, + &zone_iteration_error_cb, + NULL, + &purge_zone_iterator, + NULL, + &zone_iteration_finished, + NULL, + filter_flags); + + } + else if (list || list_orphaned) + { + if (NULL != name) + { + if (NULL == ego_name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-z", + _ ("list")); + ret = 1; + finish_command (); + return; + } + get_qe = GNUNET_NAMESTORE_records_lookup (ns, + &zone_pkey, + name, + &lookup_error_cb, + NULL, + &display_record_lookup, + NULL); + } + else + list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns, + (NULL == ego_name) ? + NULL : &zone_pkey, + &zone_iteration_error_cb, + NULL, + &display_record_iterator, + NULL, + &zone_iteration_finished, + NULL, + filter_flags); + } + if (NULL != reverse_pkey) + { + struct GNUNET_CRYPTO_PublicKey pubkey; + + if (NULL == ego_name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-z", + _ ("reverse-pkey")); + ret = 1; + finish_command (); + return; + } + if (GNUNET_OK != + GNUNET_CRYPTO_public_key_from_string (reverse_pkey, + &pubkey)) + { + fprintf (stderr, + _ ("Invalid public key for reverse lookup `%s'\n"), + reverse_pkey); + ret = 1; + finish_command (); + return; + } + reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns, + &zone_pkey, + &pubkey, + &reverse_error_cb, + NULL, + &handle_reverse_lookup, + NULL); + } + if (NULL != uri) + { + char sh[105]; + char sname[64]; + struct GNUNET_CRYPTO_PublicKey pkey; + if (NULL == ego_name) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-z", + _ ("uri")); + ret = 1; + finish_command (); + return; + } + + memset (sh, 0, 105); + memset (sname, 0, 64); + + if ((2 != (sscanf (uri, "gnunet://gns/%58s/%63s", sh, sname))) || + (GNUNET_OK != + GNUNET_CRYPTO_public_key_from_string (sh, &pkey))) + { + fprintf (stderr, _ ("Invalid URI `%s'\n"), uri); + ret = 1; + finish_command (); + return; + } + if (NULL == expirationstring) + { + fprintf (stderr, + _ ("Missing option `%s' for operation `%s'\n"), + "-e", + _ ("add")); + ret = 1; + finish_command (); + return; + } + if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime)) + { + fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring); + ret = 1; + finish_command (); + return; + } + memset (&rd, 0, sizeof(rd)); + rd.data = &pkey; + rd.data_size = GNUNET_CRYPTO_public_key_get_length (&pkey); + rd.record_type = ntohl (pkey.type); + rd.expiration_time = etime; + if (GNUNET_YES == etime_is_rel) + rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + if (1 == is_shadow) + rd.flags |= GNUNET_GNSRECORD_RF_SHADOW; + add_qe_uri = GNUNET_NAMESTORE_records_store (ns, + &zone_pkey, + sname, + 1, + &rd, + &add_continuation, + &add_qe_uri); + } + if (monitor) + { + zm = GNUNET_NAMESTORE_zone_monitor_start2 (cfg, + (NULL != ego_name) ? + &zone_pkey : NULL, + GNUNET_YES, + &monitor_error_cb, + NULL, + &display_record_monitor, + NULL, + &sync_cb, + NULL, + filter_flags); + } +} + +#define MAX_LINE_LEN 4086 + +#define MAX_ARGS 20 + +static int +get_identity_for_string (const char *str, + struct GNUNET_CRYPTO_PrivateKey *zk) +{ + const struct GNUNET_CRYPTO_PrivateKey *privkey; + struct GNUNET_CRYPTO_PublicKey pubkey; + struct GNUNET_CRYPTO_PublicKey ego_pubkey; + struct EgoEntry *ego_entry; + + if (GNUNET_OK == GNUNET_CRYPTO_public_key_from_string (str, + &pubkey)) + { + for (ego_entry = ego_head; + NULL != ego_entry; ego_entry = ego_entry->next) + { + privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &ego_pubkey); + if (0 == memcmp (&ego_pubkey, &pubkey, sizeof (pubkey))) + { + *zk = *privkey; + return GNUNET_OK; + } + } + } + else + { + for (ego_entry = ego_head; NULL != ego_entry; ego_entry = ego_entry->next) + { + /** FIXME: Check for zTLD? **/ + if (0 != strcmp (str, ego_entry->identifier)) + continue; + *zk = *GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + return GNUNET_OK; + } + } + return GNUNET_NO; +} + +static void +process_command_stdin () +{ + char buf[MAX_LINE_LEN]; + static struct GNUNET_CRYPTO_PrivateKey next_zone_key; + static char next_name[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; + static int finished = GNUNET_NO; + static int have_next_zonekey = GNUNET_NO; + int zonekey_set = GNUNET_NO; + char *tmp; + + + if (GNUNET_YES == have_next_zonekey) + { + zone_pkey = next_zone_key; + if (NULL != name) + GNUNET_free (name); + name = GNUNET_strdup (next_name); + zonekey_set = GNUNET_YES; + } + while (NULL != fgets (buf, sizeof (buf), stdin)) + { + if (1 >= strlen (buf)) + continue; + if (buf[strlen (buf) - 1] == '\n') + buf[strlen (buf) - 1] = '\0'; + /** + * Check if this is a new name. If yes, and we have records, store them. + */ + if (buf[strlen (buf) - 1] == ':') + { + memset (next_name, 0, sizeof (next_name)); + strncpy (next_name, buf, strlen (buf) - 1); + tmp = strchr (next_name, '.'); + if (NULL == tmp) + { + fprintf (stderr, "Error parsing name `%s'\n", next_name); + ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, &commit_cb, NULL); + ret = 1; + return; + } + if (GNUNET_OK != get_identity_for_string (tmp + 1, &next_zone_key)) + { + fprintf (stderr, "Error parsing zone name `%s'\n", tmp + 1); + ret = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + *tmp = '\0'; + have_next_zonekey = GNUNET_YES; + /* Run a command for the previous record set */ + if (NULL != recordset) + { + run_with_zone_pkey (cfg); + return; + } + zone_pkey = next_zone_key; + if (NULL != name) + GNUNET_free (name); + name = GNUNET_strdup (next_name); + zonekey_set = GNUNET_YES; + continue; + } + if (GNUNET_NO == zonekey_set) + { + fprintf (stderr, "Warning, encountered recordline without zone\n"); + continue; + } + parse_recordline (buf); + } + if (GNUNET_NO == finished) + { + if (NULL != recordset) + { + if (GNUNET_YES == zonekey_set) + { + run_with_zone_pkey (cfg); /** one last time **/ + finished = GNUNET_YES; + return; + } + fprintf (stderr, "Warning, encountered recordline without zone\n"); + } + } + ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, &commit_cb, NULL); + return; +} + + +static void +begin_cb (void *cls, enum GNUNET_ErrorCode ec) +{ + ns_qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start transaction: %s\n", + GNUNET_ErrorCode_get_hint (ec)); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (read_from_stdin) + { + process_command_stdin (); + return; + } + run_with_zone_pkey (cfg); +} + + +/** + * Function called with ALL of the egos known to the + * identity service, used on startup if the user did + * not specify a zone on the command-line. + * Once the iteration is done (@a ego is NULL), we + * ask for the default ego for "namestore". + * + * @param cls a `struct GNUNET_CONFIGURATION_Handle` + * @param ego an ego, NULL for end of iteration + * @param ctx NULL + * @param name name associated with @a ego + */ +static void +id_connect_cb (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *name) +{ + struct GNUNET_CRYPTO_PublicKey pk; + struct EgoEntry *ego_entry; + + (void) ctx; + (void) name; + if ((NULL != name) && (NULL != ego)) + { + ego_entry = GNUNET_new (struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->ego = ego; + ego_entry->identifier = GNUNET_strdup (name); + GNUNET_CONTAINER_DLL_insert_tail (ego_head, + ego_tail, + ego_entry); + if ((NULL != ego_name) && + (0 == strcmp (name, ego_name))) + zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); + return; + } + if (NULL != ego) + return; + ns_qe = GNUNET_NAMESTORE_transaction_begin (ns, &begin_cb, (void *) cfg); +} + + + + + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *_cfg) +{ + (void) cls; + (void) args; + (void) cfgfile; + cfg = _cfg; + if (NULL != args[0]) + GNUNET_log ( + GNUNET_ERROR_TYPE_WARNING, + _ ("Superfluous command line arguments (starting with `%s') ignored\n"), + args[0]); + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg); + ns = GNUNET_NAMESTORE_connect (cfg); + if (NULL == ns) + { + fprintf (stderr, _ ("Failed to connect to namestore\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + idh = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, (void *) cfg); + if (NULL == idh) + { + ret = -1; + fprintf (stderr, _ ("Cannot connect to identity service\n")); + GNUNET_SCHEDULER_shutdown (); + } +} + + + +/** + * The main function for gnunet-namestore. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + int lret; + struct GNUNET_GETOPT_CommandLineOption options[] = + { GNUNET_GETOPT_option_flag ('a', "add", gettext_noop ("add record"), &add), + GNUNET_GETOPT_option_flag ('d', + "delete", + gettext_noop ("delete record"), + &del), + GNUNET_GETOPT_option_flag ('D', + "display", + gettext_noop ("display records"), + &list), + GNUNET_GETOPT_option_flag ('S', + "from-stdin", + gettext_noop ("read commands from stdin"), + &read_from_stdin), + GNUNET_GETOPT_option_string ( + 'e', + "expiration", + "TIME", + gettext_noop ( + "expiration time for record to use (for adding only), \"never\" is possible"), + &expirationstring), + GNUNET_GETOPT_option_string ('i', + "nick", + "NICKNAME", + gettext_noop ( + "set the desired nick name for the zone"), + &nickstring), + GNUNET_GETOPT_option_flag ('m', + "monitor", + gettext_noop ( + "monitor changes in the namestore"), + &monitor), + GNUNET_GETOPT_option_string ('n', + "name", + "NAME", + gettext_noop ( + "name of the record to add/delete/display"), + &name), + GNUNET_GETOPT_option_flag ('r', + "recordline", + gettext_noop ("Output in recordline format"), + &output_recordline), + GNUNET_GETOPT_option_string ('Z', + "zone-to-name", + "KEY", + gettext_noop ( + "determine our name for the given KEY"), + &reverse_pkey), + GNUNET_GETOPT_option_string ('t', + "type", + "TYPE", + gettext_noop ( + "type of the record to add/delete/display"), + &typestring), + GNUNET_GETOPT_option_string ('u', + "uri", + "URI", + gettext_noop ("URI to import into our zone"), + &uri), + GNUNET_GETOPT_option_string ('V', + "value", + "VALUE", + gettext_noop ( + "value of the record to add/delete"), + &value), + GNUNET_GETOPT_option_flag ('p', + "public", + gettext_noop ("create or list public record"), + &is_public), + GNUNET_GETOPT_option_flag ('o', + "omit-private", + gettext_noop ("omit private records"), + &omit_private), + GNUNET_GETOPT_option_flag ('T', + "include-maintenance", + gettext_noop ( + "do not filter maintenance records"), + &include_maintenance), + GNUNET_GETOPT_option_flag ('P', + "purge-orphans", + gettext_noop ( + "purge namestore of all orphans"), + &purge_orphaned), + GNUNET_GETOPT_option_flag ('O', + "list-orphans", + gettext_noop ( + "show private key for orphaned records for recovery using `gnunet-identity -C -P '. Use in combination with --display"), + &list_orphaned), + GNUNET_GETOPT_option_flag ('X', + "purge-zone-records", + gettext_noop ( + "delete all records in specified zone"), + &purge_zone), + GNUNET_GETOPT_option_flag ( + 's', + "shadow", + gettext_noop ( + "create shadow record (only valid if all other records of the same type have expired"), + &is_shadow), + GNUNET_GETOPT_option_string ('z', + "zone", + "EGO", + gettext_noop ( + "name of the ego controlling the zone"), + &ego_name), + GNUNET_GETOPT_OPTION_END }; + + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + is_public = -1; + is_shadow = -1; + GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL); + if (GNUNET_OK != + (lret = GNUNET_PROGRAM_run (argc, + argv, + "gnunet-namestore", + _ ("GNUnet zone manipulation tool"), + options, + &run, + NULL))) + { + GNUNET_free_nz ((void *) argv); + // FIXME + // GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey); + return lret; + } + GNUNET_free_nz ((void *) argv); + // FIXME + // GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey); + return ret; +} + + +/* end of gnunet-namestore.c */ diff --git a/src/cli/namestore/gnunet-zoneimport.c b/src/cli/namestore/gnunet-zoneimport.c new file mode 100644 index 000000000..c7e0cf65f --- /dev/null +++ b/src/cli/namestore/gnunet-zoneimport.c @@ -0,0 +1,1872 @@ +/* + This file is part of GNUnet + Copyright (C) 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file src/namestore/gnunet-zoneimport.c + * @brief import a DNS zone for publication in GNS, incremental + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include +#include + + +/** + * Maximum number of queries pending at the same time. + */ +#define THRESH 100 + +/** + * TIME_THRESH is in usecs. How quickly do we submit fresh queries. + * Used as an additional throttle. + */ +#define TIME_THRESH 10 + +/** + * How often do we retry a query before giving up for good? + */ +#define MAX_RETRIES 5 + +/** + * How many DNS requests do we at most issue in rapid series? + */ +#define MAX_SERIES 10 + +/** + * How long do we wait at least between series of requests? + */ +#define SERIES_DELAY \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 10) + +/** + * How long do DNS records have to last at least after being imported? + */ +static struct GNUNET_TIME_Relative minimum_expiration_time; + +/** + * How many requests do we request from NAMESTORE in one batch + * during our initial iteration? + */ +#define NS_BATCH_SIZE 1024 + +/** + * Some zones may include authoritative records for other + * zones, such as foo.com.uk or bar.com.fr. As for GNS + * each dot represents a zone cut, we then need to create a + * zone on-the-fly to capture those records properly. + */ +struct Zone +{ + /** + * Kept in a DLL. + */ + struct Zone *next; + + /** + * Kept in a DLL. + */ + struct Zone *prev; + + /** + * Domain of the zone (i.e. "fr" or "com.fr") + */ + char *domain; + + /** + * Private key of the zone. + */ + struct GNUNET_CRYPTO_PrivateKey key; +}; + + +/** + * Record for the request to be stored by GNS. + */ +struct Record +{ + /** + * Kept in a DLL. + */ + struct Record *next; + + /** + * Kept in a DLL. + */ + struct Record *prev; + + /** + * GNS record. + */ + struct GNUNET_GNSRECORD_Data grd; +}; + + +/** + * Request we should make. We keep this struct in memory per request, + * thus optimizing it is crucial for the overall memory consumption of + * the zone importer. + */ +struct Request +{ + /** + * Requests are kept in a heap while waiting to be resolved. + */ + struct GNUNET_CONTAINER_HeapNode *hn; + + /** + * Active requests are kept in a DLL. + */ + struct Request *next; + + /** + * Active requests are kept in a DLL. + */ + struct Request *prev; + + /** + * Head of records that should be published in GNS for + * this hostname. + */ + struct Record *rec_head; + + /** + * Tail of records that should be published in GNS for + * this hostname. + */ + struct Record *rec_tail; + + /** + * Socket used to make the request, NULL if not active. + */ + struct GNUNET_DNSSTUB_RequestSocket *rs; + + /** + * Hostname we are resolving, allocated at the end of + * this struct (optimizing memory consumption by reducing + * total number of allocations). + */ + char *hostname; + + /** + * Namestore operation pending for this record. + */ + struct GNUNET_NAMESTORE_QueueEntry *qe; + + /** + * Zone responsible for this request. + */ + const struct Zone *zone; + + /** + * At what time does the (earliest) of the returned records + * for this name expire? At this point, we need to re-fetch + * the record. + */ + struct GNUNET_TIME_Absolute expires; + + /** + * While we are fetching the record, the value is set to the + * starting time of the DNS operation. While doing a + * NAMESTORE store, again set to the start time of the + * NAMESTORE operation. + */ + struct GNUNET_TIME_Absolute op_start_time; + + /** + * How often did we issue this query? (And failed, reset + * to zero once we were successful.) + */ + unsigned int issue_num; + + /** + * random 16-bit DNS query identifier. + */ + uint16_t id; +}; + + +/** + * Command-line argument specifying desired size of the hash map with + * all of our pending names. Usually, we use an automatically growing + * map, but this is only OK up to about a million entries. Above that + * number, the user must explicitly specify the size at startup. + */ +static unsigned int map_size = 1024; + +/** + * Handle to the identity service. + */ +static struct GNUNET_IDENTITY_Handle *id; + +/** + * Namestore handle. + */ +static struct GNUNET_NAMESTORE_Handle *ns; + +/** + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Context for DNS resolution. + */ +static struct GNUNET_DNSSTUB_Context *ctx; + +/** + * The number of DNS queries that are outstanding + */ +static unsigned int pending; + +/** + * The number of NAMESTORE record store operations that are outstanding + */ +static unsigned int pending_rs; + +/** + * Number of lookups we performed overall. + */ +static unsigned int lookups; + +/** + * Number of records we had cached. + */ +static unsigned int cached; + +/** + * How many hostnames did we reject (malformed). + */ +static unsigned int rejects; + +/** + * Number of lookups that failed. + */ +static unsigned int failures; + +/** + * Number of records we found. + */ +static unsigned int records; + +/** + * Number of record sets given to namestore. + */ +static unsigned int record_sets; + +/** + * Heap of all requests to perform, sorted by + * the time we should next do the request (i.e. by expires). + */ +static struct GNUNET_CONTAINER_Heap *req_heap; + +/** + * Active requests are kept in a DLL. + */ +static struct Request *req_head; + +/** + * Active requests are kept in a DLL. + */ +static struct Request *req_tail; + +/** + * Main task. + */ +static struct GNUNET_SCHEDULER_Task *t; + +/** + * Hash map of requests for which we may still get a response from + * the namestore. Set to NULL once the initial namestore iteration + * is done. + */ +static struct GNUNET_CONTAINER_MultiHashMap *ns_pending; + +/** + * Current zone iteration handle. + */ +static struct GNUNET_NAMESTORE_ZoneIterator *zone_it; + +/** + * Head of list of zones we are managing. + */ +static struct Zone *zone_head; + +/** + * Tail of list of zones we are managing. + */ +static struct Zone *zone_tail; + +/** + * After how many more results must #ns_lookup_result_cb() ask + * the namestore for more? + */ +static uint64_t ns_iterator_trigger_next; + +/** + * Number of DNS requests counted in latency total. + */ +static uint64_t total_dns_latency_cnt; + +/** + * Sum of DNS latencies observed. + */ +static struct GNUNET_TIME_Relative total_dns_latency; + +/** + * Number of records processed (DNS lookup, no NAMESTORE) in total. + */ +static uint64_t total_reg_proc_dns; + +/** + * Number of records processed (DNS lookup, with NAMESTORE) in total. + */ +static uint64_t total_reg_proc_dns_ns; + +/** + * Start time of the regular processing. + */ +static struct GNUNET_TIME_Absolute start_time_reg_proc; + +/** + * Last time we worked before going idle. + */ +static struct GNUNET_TIME_Absolute sleep_time_reg_proc; + +/** + * Time we slept just waiting for work. + */ +static struct GNUNET_TIME_Relative idle_time; + + +/** + * Callback for #for_all_records + * + * @param cls closure + * @param rec a DNS record + */ +typedef void (*RecordProcessor) (void *cls, + const struct GNUNET_DNSPARSER_Record *rec); + + +/** + * Call @a rp for each record in @a p, regardless of + * what response section it is in. + * + * @param p packet from DNS + * @param rp function to call + * @param rp_cls closure for @a rp + */ +static void +for_all_records (const struct GNUNET_DNSPARSER_Packet *p, + RecordProcessor rp, + void *rp_cls) +{ + for (unsigned int i = 0; i < p->num_answers; i++) + { + struct GNUNET_DNSPARSER_Record *rs = &p->answers[i]; + + rp (rp_cls, rs); + } + for (unsigned int i = 0; i < p->num_authority_records; i++) + { + struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i]; + + rp (rp_cls, rs); + } + for (unsigned int i = 0; i < p->num_additional_records; i++) + { + struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i]; + + rp (rp_cls, rs); + } +} + + +/** + * Return just the label of the hostname in @a req. + * + * @param req request to process hostname of + * @return statically allocated pointer to the label, + * overwritten upon the next request! + */ +static const char * +get_label (struct Request *req) +{ + static char label[64]; + const char *dot; + + dot = strchr (req->hostname, (unsigned char) '.'); + if (NULL == dot) + { + GNUNET_break (0); + return NULL; + } + if (((size_t) (dot - req->hostname)) >= sizeof(label)) + { + GNUNET_break (0); + return NULL; + } + GNUNET_memcpy (label, req->hostname, dot - req->hostname); + label[dot - req->hostname] = '\0'; + return label; +} + + +/** + * Build DNS query for @a hostname. + * + * @param hostname host to build query for + * @param[out] raw_size number of bytes in the query + * @return NULL on error, otherwise pointer to statically (!) + * allocated query buffer + */ +static void * +build_dns_query (struct Request *req, size_t *raw_size) +{ + static char raw[512]; + char *rawp; + struct GNUNET_DNSPARSER_Packet p; + struct GNUNET_DNSPARSER_Query q; + int ret; + + q.name = (char *) req->hostname; + q.type = GNUNET_DNSPARSER_TYPE_NS; + q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET; + + memset (&p, 0, sizeof(p)); + p.num_queries = 1; + p.queries = &q; + p.id = req->id; + ret = GNUNET_DNSPARSER_pack (&p, UINT16_MAX, &rawp, raw_size); + if (GNUNET_OK != ret) + { + if (GNUNET_NO == ret) + GNUNET_free (rawp); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to pack query for hostname `%s'\n", + req->hostname); + rejects++; + return NULL; + } + if (*raw_size > sizeof(raw)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to pack query for hostname `%s'\n", + req->hostname); + rejects++; + GNUNET_break (0); + GNUNET_free (rawp); + return NULL; + } + GNUNET_memcpy (raw, rawp, *raw_size); + GNUNET_free (rawp); + return raw; +} + + +/** + * Free records associated with @a req. + * + * @param req request to free records of + */ +static void +free_records (struct Request *req) +{ + struct Record *rec; + + /* Free records */ + while (NULL != (rec = req->rec_head)) + { + GNUNET_CONTAINER_DLL_remove (req->rec_head, req->rec_tail, rec); + GNUNET_free (rec); + } +} + + +/** + * Free @a req and data structures reachable from it. + * + * @param req request to free + */ +static void +free_request (struct Request *req) +{ + free_records (req); + GNUNET_free (req); +} + + +/** + * Process as many requests as possible from the queue. + * + * @param cls NULL + */ +static void +process_queue (void *cls); + + +/** + * Insert @a req into DLL sorted by next fetch time. + * + * @param req request to insert into #req_heap + */ +static void +insert_sorted (struct Request *req) +{ + req->hn = + GNUNET_CONTAINER_heap_insert (req_heap, req, req->expires.abs_value_us); + if (req == GNUNET_CONTAINER_heap_peek (req_heap)) + { + if (NULL != t) + GNUNET_SCHEDULER_cancel (t); + sleep_time_reg_proc = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL); + } +} + + +/** + * Add record to the GNS record set for @a req. + * + * @param req the request to expand GNS record set for + * @param type type to use + * @param expiration_time when should @a rec expire + * @param data raw data to store + * @param data_len number of bytes in @a data + */ +static void +add_record (struct Request *req, + uint32_t type, + struct GNUNET_TIME_Absolute expiration_time, + const void *data, + size_t data_len) +{ + struct Record *rec; + + rec = GNUNET_malloc (sizeof(struct Record) + data_len); + rec->grd.data = &rec[1]; + rec->grd.expiration_time = expiration_time.abs_value_us; + rec->grd.data_size = data_len; + rec->grd.record_type = type; + rec->grd.flags = GNUNET_GNSRECORD_RF_NONE; + GNUNET_memcpy (&rec[1], data, data_len); + GNUNET_CONTAINER_DLL_insert (req->rec_head, req->rec_tail, rec); +} + + +/** + * Closure for #check_for_glue. + */ +struct GlueClosure +{ + /** + * Overall request we are processing. + */ + struct Request *req; + + /** + * NS name we are looking for glue for. + */ + const char *ns; + + /** + * Set to #GNUNET_YES if glue was found. + */ + int found; +}; + + +/** + * Try to find glue records for a given NS record. + * + * @param cls a `struct GlueClosure *` + * @param rec record that may contain glue information + */ +static void +check_for_glue (void *cls, const struct GNUNET_DNSPARSER_Record *rec) +{ + struct GlueClosure *gc = cls; + char dst[65536]; + size_t dst_len; + size_t off; + char ip[INET6_ADDRSTRLEN + 1]; + socklen_t ip_size = (socklen_t) sizeof(ip); + struct GNUNET_TIME_Absolute expiration_time; + struct GNUNET_TIME_Relative left; + + if (0 != strcasecmp (rec->name, gc->ns)) + return; + expiration_time = rec->expiration_time; + left = GNUNET_TIME_absolute_get_remaining (expiration_time); + if (0 == left.rel_value_us) + return; /* ignore expired glue records */ + /* if expiration window is too short, bump it to configured minimum */ + if (left.rel_value_us < minimum_expiration_time.rel_value_us) + expiration_time = + GNUNET_TIME_relative_to_absolute (minimum_expiration_time); + dst_len = sizeof(dst); + off = 0; + switch (rec->type) + { + case GNUNET_DNSPARSER_TYPE_A: + if (sizeof(struct in_addr) != rec->data.raw.data_len) + { + GNUNET_break (0); + return; + } + if (NULL == inet_ntop (AF_INET, rec->data.raw.data, ip, ip_size)) + { + GNUNET_break (0); + return; + } + if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + gc->req->hostname)) && + (GNUNET_OK == + GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip))) + { + add_record (gc->req, + GNUNET_GNSRECORD_TYPE_GNS2DNS, + expiration_time, + dst, + off); + gc->found = GNUNET_YES; + } + break; + + case GNUNET_DNSPARSER_TYPE_AAAA: + if (sizeof(struct in6_addr) != rec->data.raw.data_len) + { + GNUNET_break (0); + return; + } + if (NULL == inet_ntop (AF_INET6, rec->data.raw.data, ip, ip_size)) + { + GNUNET_break (0); + return; + } + if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + gc->req->hostname)) && + (GNUNET_OK == + GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip))) + { + add_record (gc->req, + GNUNET_GNSRECORD_TYPE_GNS2DNS, + expiration_time, + dst, + off); + gc->found = GNUNET_YES; + } + break; + + case GNUNET_DNSPARSER_TYPE_CNAME: + if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + gc->req->hostname)) && + (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + rec->data.hostname))) + { + add_record (gc->req, + GNUNET_GNSRECORD_TYPE_GNS2DNS, + expiration_time, + dst, + off); + gc->found = GNUNET_YES; + } + break; + + default: + /* useless, do nothing */ + break; + } +} + + +/** + * Closure for #process_record(). + */ +struct ProcessRecordContext +{ + /** + * Answer we got back and are currently parsing, or NULL + * if not active. + */ + struct GNUNET_DNSPARSER_Packet *p; + + /** + * Request we are processing. + */ + struct Request *req; +}; + + +/** + * We received @a rec for @a req. Remember the answer. + * + * @param cls a `struct ProcessRecordContext` + * @param rec response + */ +static void +process_record (void *cls, const struct GNUNET_DNSPARSER_Record *rec) +{ + struct ProcessRecordContext *prc = cls; + struct Request *req = prc->req; + char dst[65536]; + size_t dst_len; + size_t off; + struct GNUNET_TIME_Absolute expiration_time; + struct GNUNET_TIME_Relative left; + + dst_len = sizeof(dst); + off = 0; + records++; + if (0 != strcasecmp (rec->name, req->hostname)) + { + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "DNS returned record from zone `%s' of type %u while resolving `%s'\n", + rec->name, + (unsigned int) rec->type, + req->hostname); + return; /* does not match hostname, might be glue, but + not useful for this pass! */ + } + expiration_time = rec->expiration_time; + left = GNUNET_TIME_absolute_get_remaining (expiration_time); + if (0 == left.rel_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DNS returned expired record for `%s'\n", + req->hostname); + GNUNET_STATISTICS_update (stats, + "# expired records obtained from DNS", + 1, + GNUNET_NO); + return; /* record expired */ + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DNS returned record that expires at %s for `%s'\n", + GNUNET_STRINGS_absolute_time_to_string (expiration_time), + req->hostname); + /* if expiration window is too short, bump it to configured minimum */ + if (left.rel_value_us < minimum_expiration_time.rel_value_us) + expiration_time = + GNUNET_TIME_relative_to_absolute (minimum_expiration_time); + switch (rec->type) + { + case GNUNET_DNSPARSER_TYPE_NS: { + struct GlueClosure gc; + + /* check for glue */ + gc.req = req; + gc.ns = rec->data.hostname; + gc.found = GNUNET_NO; + for_all_records (prc->p, &check_for_glue, &gc); + if ((GNUNET_NO == gc.found) && + (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + req->hostname)) && + (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + rec->data.hostname))) + { + /* FIXME: actually check if this is out-of-bailiwick, + and if not request explicit resolution... */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converted OOB (`%s') NS record for `%s'\n", + rec->data.hostname, + rec->name); + add_record (req, + GNUNET_GNSRECORD_TYPE_GNS2DNS, + expiration_time, + dst, + off); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converted NS record for `%s' using glue\n", + rec->name); + } + break; + } + + case GNUNET_DNSPARSER_TYPE_CNAME: + if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + rec->data.hostname)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converting CNAME (`%s') record for `%s'\n", + rec->data.hostname, + rec->name); + add_record (req, rec->type, expiration_time, dst, off); + } + break; + + case GNUNET_DNSPARSER_TYPE_DNAME: + /* No support for DNAME in GNS yet! FIXME: support later! */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "FIXME: not supported: %s DNAME %s\n", + rec->name, + rec->data.hostname); + break; + + case GNUNET_DNSPARSER_TYPE_MX: + if (GNUNET_OK == + GNUNET_DNSPARSER_builder_add_mx (dst, dst_len, &off, rec->data.mx)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converting MX (`%s') record for `%s'\n", + rec->data.mx->mxhost, + rec->name); + add_record (req, rec->type, expiration_time, dst, off); + } + break; + + case GNUNET_DNSPARSER_TYPE_SOA: + if (GNUNET_OK == + GNUNET_DNSPARSER_builder_add_soa (dst, dst_len, &off, rec->data.soa)) + { + /* NOTE: GNS does not really use SOAs */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converting SOA record for `%s'\n", + rec->name); + add_record (req, rec->type, expiration_time, dst, off); + } + break; + + case GNUNET_DNSPARSER_TYPE_SRV: + if (GNUNET_OK == + GNUNET_DNSPARSER_builder_add_srv (dst, dst_len, &off, rec->data.srv)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converting SRV record for `%s'\n", + rec->name); + add_record (req, rec->type, expiration_time, dst, off); + } + break; + + case GNUNET_DNSPARSER_TYPE_PTR: + if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, + dst_len, + &off, + rec->data.hostname)) + { + /* !?: what does a PTR record do in a regular TLD??? */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converting PTR record for `%s' (weird)\n", + rec->name); + add_record (req, rec->type, expiration_time, dst, off); + } + break; + + case GNUNET_DNSPARSER_TYPE_CERT: + if (GNUNET_OK == + GNUNET_DNSPARSER_builder_add_cert (dst, dst_len, &off, rec->data.cert)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converting CERT record for `%s'\n", + rec->name); + add_record (req, rec->type, expiration_time, dst, off); + } + break; + + /* Rest is 'raw' encoded and just needs to be copied IF + the hostname matches the requested name; otherwise we + simply cannot use it. */ + case GNUNET_DNSPARSER_TYPE_A: + case GNUNET_DNSPARSER_TYPE_AAAA: + case GNUNET_DNSPARSER_TYPE_TXT: + default: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Converting record of type %u for `%s'\n", + (unsigned int) rec->type, + rec->name); + add_record (req, + rec->type, + expiration_time, + rec->data.raw.data, + rec->data.raw.data_len); + break; + } +} + + +static void +store_completed_cb (void *cls, enum GNUNET_ErrorCode ec) +{ + static struct GNUNET_TIME_Absolute last; + struct Request *req = cls; + + req->qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store zone data for `%s': %s\n", + req->hostname, + GNUNET_ErrorCode_get_hint (ec)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stored records under `%s' (%d)\n", + req->hostname, + ec); + } + total_reg_proc_dns_ns++; /* finished regular processing */ + pending_rs--; + free_records (req); + /* compute NAMESTORE statistics */ + { + static uint64_t total_ns_latency_cnt; + static struct GNUNET_TIME_Relative total_ns_latency; + struct GNUNET_TIME_Relative ns_latency; + + ns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time); + total_ns_latency = GNUNET_TIME_relative_add (total_ns_latency, ns_latency); + if (0 == total_ns_latency_cnt) + last = GNUNET_TIME_absolute_get (); + total_ns_latency_cnt++; + if (0 == (total_ns_latency_cnt % 1000)) + { + struct GNUNET_TIME_Relative delta; + + delta = GNUNET_TIME_absolute_get_duration (last); + last = GNUNET_TIME_absolute_get (); + fprintf (stderr, + "Processed 1000 records in %s\n", + GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES)); + GNUNET_STATISTICS_set (stats, + "# average NAMESTORE PUT latency (μs)", + total_ns_latency.rel_value_us + / total_ns_latency_cnt, + GNUNET_NO); + } + } + /* compute and publish overall velocity */ + if (0 == (total_reg_proc_dns_ns % 100)) + { + struct GNUNET_TIME_Relative runtime; + + runtime = GNUNET_TIME_absolute_get_duration (start_time_reg_proc); + runtime = GNUNET_TIME_relative_subtract (runtime, idle_time); + runtime = + GNUNET_TIME_relative_divide (runtime, + total_reg_proc_dns + total_reg_proc_dns_ns); + GNUNET_STATISTICS_set (stats, + "# Regular processing completed without NAMESTORE", + total_reg_proc_dns, + GNUNET_NO); + GNUNET_STATISTICS_set (stats, + "# Regular processing completed with NAMESTORE PUT", + total_reg_proc_dns_ns, + GNUNET_NO); + GNUNET_STATISTICS_set (stats, + "# average request processing latency (μs)", + runtime.rel_value_us, + GNUNET_NO); + GNUNET_STATISTICS_set (stats, + "# total time spent idle (μs)", + idle_time.rel_value_us, + GNUNET_NO); + } + + if (NULL == t) + { + sleep_time_reg_proc = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_now (&process_queue, NULL); + } +} + + +/** + * Function called with the result of a DNS resolution. + * + * @param cls closure with the `struct Request` + * @param dns dns response, never NULL + * @param dns_len number of bytes in @a dns + */ +static void +process_result (void *cls, + const struct GNUNET_TUN_DnsHeader *dns, + size_t dns_len) +{ + struct Request *req = cls; + struct Record *rec; + struct GNUNET_DNSPARSER_Packet *p; + unsigned int rd_count; + + GNUNET_assert (NULL == req->hn); + if (NULL == dns) + { + /* stub gave up */ + GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req); + pending--; + if (NULL == t) + { + sleep_time_reg_proc = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_now (&process_queue, NULL); + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Stub gave up on DNS reply for `%s'\n", + req->hostname); + GNUNET_STATISTICS_update (stats, "# DNS lookups timed out", 1, GNUNET_NO); + if (req->issue_num > MAX_RETRIES) + { + failures++; + free_request (req); + GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO); + return; + } + total_reg_proc_dns++; + req->rs = NULL; + insert_sorted (req); + return; + } + if (req->id != dns->id) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DNS ID did not match request, ignoring reply\n"); + GNUNET_STATISTICS_update (stats, "# DNS ID mismatches", 1, GNUNET_NO); + return; + } + GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req); + GNUNET_DNSSTUB_resolve_cancel (req->rs); + req->rs = NULL; + pending--; + p = GNUNET_DNSPARSER_parse ((const char *) dns, dns_len); + if (NULL == p) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse DNS reply for `%s'\n", + req->hostname); + GNUNET_STATISTICS_update (stats, "# DNS parser errors", 1, GNUNET_NO); + if (NULL == t) + { + sleep_time_reg_proc = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_now (&process_queue, NULL); + } + if (req->issue_num > MAX_RETRIES) + { + failures++; + free_request (req); + GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO); + return; + } + insert_sorted (req); + return; + } + /* import new records */ + req->issue_num = 0; /* success, reset counter! */ + { + struct ProcessRecordContext prc = { .req = req, .p = p }; + + for_all_records (p, &process_record, &prc); + } + GNUNET_DNSPARSER_free_packet (p); + /* count records found, determine minimum expiration time */ + req->expires = GNUNET_TIME_UNIT_FOREVER_ABS; + { + struct GNUNET_TIME_Relative dns_latency; + + dns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time); + total_dns_latency = + GNUNET_TIME_relative_add (total_dns_latency, dns_latency); + total_dns_latency_cnt++; + if (0 == (total_dns_latency_cnt % 1000)) + { + GNUNET_STATISTICS_set (stats, + "# average DNS lookup latency (μs)", + total_dns_latency.rel_value_us + / total_dns_latency_cnt, + GNUNET_NO); + } + } + rd_count = 0; + for (rec = req->rec_head; NULL != rec; rec = rec->next) + { + struct GNUNET_TIME_Absolute at; + + at.abs_value_us = rec->grd.expiration_time; + req->expires = GNUNET_TIME_absolute_min (req->expires, at); + rd_count++; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Obtained %u records for `%s'\n", + rd_count, + req->hostname); + /* Instead of going for SOA, simplified for now to look each + day in case we got an empty response */ + if (0 == rd_count) + { + req->expires = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS); + GNUNET_STATISTICS_update (stats, + "# empty DNS replies (usually NXDOMAIN)", + 1, + GNUNET_NO); + } + else + { + record_sets++; + } + /* convert records to namestore import format */ + { + struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; + unsigned int off = 0; + + /* convert linked list into array */ + for (rec = req->rec_head; NULL != rec; rec = rec->next) + rd[off++] = rec->grd; + pending_rs++; + req->op_start_time = GNUNET_TIME_absolute_get (); + req->qe = GNUNET_NAMESTORE_records_store (ns, + &req->zone->key, + get_label (req), + rd_count, + rd, + &store_completed_cb, + req); + GNUNET_assert (NULL != req->qe); + } + insert_sorted (req); +} + + +/** + * Process as many requests as possible from the queue. + * + * @param cls NULL + */ +static void +process_queue (void *cls) +{ + struct Request *req; + unsigned int series; + void *raw; + size_t raw_size; + struct GNUNET_TIME_Relative delay; + + (void) cls; + delay = GNUNET_TIME_absolute_get_duration (sleep_time_reg_proc); + idle_time = GNUNET_TIME_relative_add (idle_time, delay); + series = 0; + t = NULL; + while (pending + pending_rs < THRESH) + { + req = GNUNET_CONTAINER_heap_peek (req_heap); + if (NULL == req) + break; + if (NULL != req->qe) + return; /* namestore op still pending */ + if (NULL != req->rs) + { + GNUNET_break (0); + return; /* already submitted */ + } + if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0) + break; + GNUNET_assert (req == GNUNET_CONTAINER_heap_remove_root (req_heap)); + req->hn = NULL; + GNUNET_CONTAINER_DLL_insert (req_head, req_tail, req); + GNUNET_assert (NULL == req->rs); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting resolution for `%s'\n", + req->hostname); + raw = build_dns_query (req, &raw_size); + if (NULL == raw) + { + GNUNET_break (0); + free_request (req); + continue; + } + req->op_start_time = GNUNET_TIME_absolute_get (); + req->rs = GNUNET_DNSSTUB_resolve (ctx, raw, raw_size, &process_result, req); + GNUNET_assert (NULL != req->rs); + req->issue_num++; + lookups++; + pending++; + series++; + if (series > MAX_SERIES) + break; + } + if (pending + pending_rs >= THRESH) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopped processing queue (%u+%u/%u)]\n", + pending, + pending_rs, + THRESH); + return; /* wait for replies */ + } + req = GNUNET_CONTAINER_heap_peek (req_heap); + if (NULL == req) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopped processing queue: empty queue\n"); + return; + } + if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Waiting until %s for next record (`%s') to expire\n", + GNUNET_STRINGS_absolute_time_to_string (req->expires), + req->hostname); + if (NULL != t) + GNUNET_SCHEDULER_cancel (t); + sleep_time_reg_proc = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Throttling\n"); + if (NULL != t) + GNUNET_SCHEDULER_cancel (t); + sleep_time_reg_proc = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_delayed (SERIES_DELAY, &process_queue, NULL); +} + + +/** + * Iterator called during #do_shutdown() to free requests in + * the #ns_pending map. + * + * @param cls NULL + * @param key unused + * @param value the `struct Request` to free + * @return #GNUNET_OK + */ +static int +free_request_it (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct Request *req = value; + + (void) cls; + (void) key; + free_request (req); + return GNUNET_OK; +} + + +/** + * Clean up and terminate the process. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + struct Request *req; + struct Zone *zone; + + (void) cls; + if (NULL != id) + { + GNUNET_IDENTITY_disconnect (id); + id = NULL; + } + if (NULL != t) + { + GNUNET_SCHEDULER_cancel (t); + t = NULL; + } + while (NULL != (req = req_head)) + { + GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req); + if (NULL != req->qe) + GNUNET_NAMESTORE_cancel (req->qe); + free_request (req); + } + while (NULL != (req = GNUNET_CONTAINER_heap_remove_root (req_heap))) + { + req->hn = NULL; + if (NULL != req->qe) + GNUNET_NAMESTORE_cancel (req->qe); + free_request (req); + } + if (NULL != zone_it) + { + GNUNET_NAMESTORE_zone_iteration_stop (zone_it); + zone_it = NULL; + } + if (NULL != ns) + { + GNUNET_NAMESTORE_disconnect (ns); + ns = NULL; + } + if (NULL != ctx) + { + GNUNET_DNSSTUB_stop (ctx); + ctx = NULL; + } + if (NULL != req_heap) + { + GNUNET_CONTAINER_heap_destroy (req_heap); + req_heap = NULL; + } + if (NULL != ns_pending) + { + GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &free_request_it, NULL); + GNUNET_CONTAINER_multihashmap_destroy (ns_pending); + ns_pending = NULL; + } + while (NULL != (zone = zone_head)) + { + GNUNET_CONTAINER_DLL_remove (zone_head, zone_tail, zone); + GNUNET_free (zone->domain); + GNUNET_free (zone); + } + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, GNUNET_NO); + stats = NULL; + } +} + + +/** + * Iterate over all of the zones we care about and see which records + * we may need to re-fetch when. + * + * @param cls NULL + */ +static void +iterate_zones (void *cls); + + +/** + * Function called if #GNUNET_NAMESTORE_records_lookup() failed. + * Just logs an error. + * + * @param cls a `struct Zone` + */ +static void +ns_lookup_error_cb (void *cls) +{ + struct Zone *zone = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to load data from namestore for zone `%s'\n", + zone->domain); + zone_it = NULL; + ns_iterator_trigger_next = 0; + iterate_zones (NULL); +} + + +/** + * Process a record that was stored in the namestore. + * + * @param cls a `struct Zone *` + * @param key private key of the zone + * @param label label of the records + * @param rd_count number of entries in @a rd array, 0 if label was deleted + * @param rd array of records with data to store + */ +static void +ns_lookup_result_cb (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *key, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct Zone *zone = cls; + struct Request *req; + struct GNUNET_HashCode hc; + char *fqdn; + + ns_iterator_trigger_next--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Obtained NAMESTORE reply, %llu left in round\n", + (unsigned long long) ns_iterator_trigger_next); + if (0 == ns_iterator_trigger_next) + { + ns_iterator_trigger_next = NS_BATCH_SIZE; + GNUNET_STATISTICS_update (stats, + "# NAMESTORE records requested from cache", + ns_iterator_trigger_next, + GNUNET_NO); + GNUNET_NAMESTORE_zone_iterator_next (zone_it, ns_iterator_trigger_next); + } + GNUNET_asprintf (&fqdn, "%s.%s", label, zone->domain); + GNUNET_CRYPTO_hash (fqdn, strlen (fqdn) + 1, &hc); + GNUNET_free (fqdn); + req = GNUNET_CONTAINER_multihashmap_get (ns_pending, &hc); + if (NULL == req) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Ignoring record `%s' in zone `%s': not on my list!\n", + label, + zone->domain); + return; + } + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_remove (ns_pending, &hc, req)); + GNUNET_break (0 == GNUNET_memcmp (key, &req->zone->key)); + GNUNET_break (0 == strcasecmp (label, get_label (req))); + for (unsigned int i = 0; i < rd_count; i++) + { + struct GNUNET_TIME_Absolute at; + + if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) + { + struct GNUNET_TIME_Relative rel; + + rel.rel_value_us = rd->expiration_time; + at = GNUNET_TIME_relative_to_absolute (rel); + } + else + { + at.abs_value_us = rd->expiration_time; + } + add_record (req, rd->record_type, at, rd->data, rd->data_size); + } + if (0 == rd_count) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Empty record set in namestore for `%s'\n", + req->hostname); + } + else + { + unsigned int pos = 0; + + cached++; + req->expires = GNUNET_TIME_UNIT_FOREVER_ABS; + for (struct Record *rec = req->rec_head; NULL != rec; rec = rec->next) + { + struct GNUNET_TIME_Absolute at; + + at.abs_value_us = rec->grd.expiration_time; + req->expires = GNUNET_TIME_absolute_min (req->expires, at); + pos++; + } + if (0 == pos) + req->expires = GNUNET_TIME_UNIT_ZERO_ABS; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Hot-start with %u existing records for `%s'\n", + pos, + req->hostname); + } + free_records (req); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Adding `%s' to worklist to start at %s\n", + req->hostname, + GNUNET_STRINGS_absolute_time_to_string (req->expires)); + insert_sorted (req); +} + + +/** + * Add @a hostname to the list of requests to be made. + * + * @param hostname name to resolve + */ +static void +queue (const char *hostname) +{ + struct Request *req; + const char *dot; + struct Zone *zone; + size_t hlen; + struct GNUNET_HashCode hc; + + if (GNUNET_OK != GNUNET_DNSPARSER_check_name (hostname)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Refusing invalid hostname `%s'\n", + hostname); + rejects++; + return; + } + dot = strchr (hostname, (unsigned char) '.'); + if (NULL == dot) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Refusing invalid hostname `%s' (lacks '.')\n", + hostname); + rejects++; + return; + } + for (zone = zone_head; NULL != zone; zone = zone->next) + if (0 == strcmp (zone->domain, dot + 1)) + break; + if (NULL == zone) + { + rejects++; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Domain name `%s' not in ego list!\n", + dot + 1); + return; + } + + hlen = strlen (hostname) + 1; + req = GNUNET_malloc (sizeof(struct Request) + hlen); + req->zone = zone; + req->hostname = (char *) &req[1]; + GNUNET_memcpy (req->hostname, hostname, hlen); + req->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT16_MAX); + GNUNET_CRYPTO_hash (req->hostname, hlen, &hc); + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( + ns_pending, + &hc, + req, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Duplicate hostname `%s' ignored\n", + hostname); + GNUNET_free (req); + return; + } +} + + +/** + * We have completed the initial iteration over the namestore's database. + * This function is called on each of the remaining records in + * #move_to_queue to #queue() them, as we will simply not find existing + * records for them any longer. + * + * @param cls NULL + * @param key unused + * @param value a `struct Request` + * @return #GNUNET_OK (continue to iterate) + */ +static int +move_to_queue (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct Request *req = value; + + (void) cls; + (void) key; + insert_sorted (req); + return GNUNET_OK; +} + + +/** + * Iterate over all of the zones we care about and see which records + * we may need to re-fetch when. + * + * @param cls NULL + */ +static void +iterate_zones (void *cls) +{ + static struct Zone *last; + + (void) cls; + if (NULL != zone_it) + { + zone_it = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finished iteration over zone `%s'!\n", + last->domain); + /* subtract left-overs from previous iteration */ + GNUNET_STATISTICS_update (stats, + "# NAMESTORE records requested from cache", + (long long) (-ns_iterator_trigger_next), + GNUNET_NO); + ns_iterator_trigger_next = 0; + } + GNUNET_assert (NULL != zone_tail); + if (zone_tail == last) + { + /* Done iterating over relevant zones in NAMESTORE, move + rest of hash map to work queue as well. */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finished all NAMESTORE iterations!\n"); + GNUNET_STATISTICS_set (stats, + "# Domain names without cached reply", + GNUNET_CONTAINER_multihashmap_size (ns_pending), + GNUNET_NO); + GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &move_to_queue, NULL); + GNUNET_CONTAINER_multihashmap_destroy (ns_pending); + ns_pending = NULL; + start_time_reg_proc = GNUNET_TIME_absolute_get (); + total_reg_proc_dns = 0; + total_reg_proc_dns_ns = 0; + return; + } + if (NULL == last) + last = zone_head; + else + last = last->next; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting iteration over zone `%s'!\n", + last->domain); + /* subtract left-overs from previous iteration */ + GNUNET_STATISTICS_update (stats, + "# NAMESTORE records requested from cache", + 1, + GNUNET_NO); + ns_iterator_trigger_next = 1; + GNUNET_STATISTICS_update (stats, "# zones iterated", 1, GNUNET_NO); + zone_it = GNUNET_NAMESTORE_zone_iteration_start (ns, + &last->key, + &ns_lookup_error_cb, + NULL, + &ns_lookup_result_cb, + last, + &iterate_zones, + NULL); +} + + +/** + * Begin processing hostnames from stdin. + * + * @param cls NULL + */ +static void +process_stdin (void *cls) +{ + static struct GNUNET_TIME_Absolute last; + static uint64_t idot; + char hn[256]; + + (void) cls; + t = NULL; + if (NULL != id) + { + GNUNET_IDENTITY_disconnect (id); + id = NULL; + } + while (NULL != fgets (hn, sizeof(hn), stdin)) + { + if (strlen (hn) > 0) + hn[strlen (hn) - 1] = '\0'; /* eat newline */ + if (0 == idot) + last = GNUNET_TIME_absolute_get (); + idot++; + if (0 == idot % 100000) + { + struct GNUNET_TIME_Relative delta; + + delta = GNUNET_TIME_absolute_get_duration (last); + last = GNUNET_TIME_absolute_get (); + fprintf (stderr, + "Read 100000 domain names in %s\n", + GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES)); + GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO); + } + queue (hn); + } + fprintf (stderr, + "Done reading %llu domain names\n", + (unsigned long long) idot); + GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO); + iterate_zones (NULL); +} + + +/** + * Method called to inform about the egos of this peer. + * + * When used with #GNUNET_IDENTITY_connect, this function is + * initially called for all egos and then again whenever a + * ego's name changes or if it is deleted. At the end of + * the initial pass over all egos, the function is once called + * with 'NULL' for @a ego. That does NOT mean that the callback won't + * be invoked in the future or that there was an error. + * + * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get, this + * function is only called ONCE, and 'NULL' being passed in @a ego does + * indicate an error (for example because name is taken or no default value is + * known). If @a ego is non-NULL and if '*ctx' is set in those callbacks, the + * value WILL be passed to a subsequent call to the identity callback of + * #GNUNET_IDENTITY_connect (if that one was not NULL). + * + * When an identity is renamed, this function is called with the + * (known) @a ego but the NEW @a name. + * + * When an identity is deleted, this function is called with the + * (known) ego and "NULL" for the @a name. In this case, + * the @a ego is henceforth invalid (and the @a ctx should also be + * cleaned up). + * + * @param cls closure + * @param ego ego handle, NULL for end of list + * @param ctx context for application to store data for this ego + * (during the lifetime of this process, initially NULL) + * @param name name assigned by the user for this ego, + * NULL if the user just deleted the ego and it + * must thus no longer be used + */ +static void +identity_cb (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *name) +{ + (void) cls; + (void) ctx; + + if (NULL == ego) + { + /* end of iteration */ + if (NULL == zone_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No zone found\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + /* zone_head non-null, process hostnames from stdin */ + t = GNUNET_SCHEDULER_add_now (&process_stdin, NULL); + return; + } + if (NULL != name) + { + struct Zone *zone; + + zone = GNUNET_new (struct Zone); + zone->key = *GNUNET_IDENTITY_ego_get_private_key (ego); + zone->domain = GNUNET_strdup (name); + GNUNET_CONTAINER_DLL_insert (zone_head, zone_tail, zone); + } +} + + +/** + * Process requests from the queue, then if the queue is + * not empty, try again. + * + * @param cls NULL + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + (void) cls; + (void) args; + (void) cfgfile; + stats = GNUNET_STATISTICS_create ("zoneimport", cfg); + req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + ns_pending = GNUNET_CONTAINER_multihashmap_create (map_size, GNUNET_NO); + if (NULL == ns_pending) + { + fprintf (stderr, "Failed to allocate memory for main hash map\n"); + return; + } + ctx = GNUNET_DNSSTUB_start (256); + if (NULL == ctx) + { + fprintf (stderr, "Failed to initialize GNUnet DNS STUB\n"); + return; + } + if (NULL == args[0]) + { + fprintf (stderr, + "You must provide a list of DNS resolvers on the command line\n"); + return; + } + for (unsigned int i = 0; NULL != args[i]; i++) + { + if (GNUNET_OK != GNUNET_DNSSTUB_add_dns_ip (ctx, args[i])) + { + fprintf (stderr, "Failed to use `%s' for DNS resolver\n", args[i]); + return; + } + } + + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + ns = GNUNET_NAMESTORE_connect (cfg); + if (NULL == ns) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL); +} + + +/** + * Call with IP address of resolver to query. + * + * @param argc should be 2 + * @param argv[1] should contain IP address + * @return 0 on success + */ +int +main (int argc, char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = + { GNUNET_GETOPT_option_uint ('s', + "size", + "MAPSIZE", + gettext_noop ( + "size to use for the main hash map"), + &map_size), + GNUNET_GETOPT_option_relative_time ( + 'm', + "minimum-expiration", + "RELATIVETIME", + gettext_noop ("minimum expiration time we assume for imported records"), + &minimum_expiration_time), + GNUNET_GETOPT_OPTION_END }; + int ret; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + if (GNUNET_OK != (ret = GNUNET_PROGRAM_run (argc, + argv, + "gnunet-zoneimport", + "import DNS zone into namestore", + options, + &run, + NULL))) + return ret; + GNUNET_free_nz ((void *) argv); + fprintf (stderr, + "Rejected %u names, had %u cached, did %u lookups, stored %u record sets\n" + "Found %u records, %u lookups failed, %u/%u pending on shutdown\n", + rejects, + cached, + lookups, + record_sets, + records, + failures, + pending, + pending_rs); + return 0; +} + + +/* end of gnunet-zoneimport.c */ diff --git a/src/cli/namestore/test_namestore_delete.sh b/src/cli/namestore/test_namestore_delete.sh new file mode 100755 index 000000000..b861a4bc0 --- /dev/null +++ b/src/cli/namestore/test_namestore_delete.sh @@ -0,0 +1,68 @@ +#!/bin/bash +CONFIGURATION="test_namestore_api.conf" +trap "gnunet-arm -e -c $CONFIGURATION" SIGINT + +LOCATION=$(which gnunet-config) +if [ -z $LOCATION ] +then + LOCATION="gnunet-config" +fi +$LOCATION --version 1> /dev/null +if test $? != 0 +then + echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" + exit 77 +fi + +rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` +TEST_DOMAIN_PLUS="www.gnu" +TEST_DOMAIN_DNS="www3.gnu" +TEST_IP_PLUS="127.0.0.1" +TEST_IP_DNS="131.159.74.67" +TEST_RECORD_CNAME_SERVER="server" +TEST_RECORD_CNAME_PLUS="server.+" +TEST_RECORD_CNAME_DNS="gnunet.org" +TEST_RECORD_NAME_SERVER="server" +TEST_RECORD_NAME_PLUS="www" +TEST_RECORD_NAME_DNS="www3" +which timeout &> /dev/null && DO_TIMEOUT="timeout 5" + +function start_peer +{ + gnunet-arm -s -c $CONFIGURATION + gnunet-identity -C testego -c $CONFIGURATION +} + +function stop_peer +{ + gnunet-identity -D testego -c $CONFIGURATION + gnunet-arm -e -c $CONFIGURATION +} + + +start_peer +# Create a public record +gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION +# Delete record +gnunet-namestore -p -z testego -d -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION +# List all records +OUTPUT=`gnunet-namestore -p -z testego -D` +FOUND_IP=false +FOUND_NAME=false +for LINE in $OUTPUT ; + do + if echo "$LINE" | grep -q "$TEST_RECORD_NAME_DNS"; then + FOUND_NAME=true; + fi + if echo "$LINE" | grep -q "$TEST_IP_PLUS"; then + FOUND_IP=true; + fi + done +stop_peer + + +if [ $FOUND_IP = true ] +then + echo "FAIL: Delete name in namestore: IP returned" + exit 1 +fi diff --git a/src/cli/namestore/test_namestore_lookup.sh b/src/cli/namestore/test_namestore_lookup.sh new file mode 100755 index 000000000..1c96e102a --- /dev/null +++ b/src/cli/namestore/test_namestore_lookup.sh @@ -0,0 +1,63 @@ +#!/bin/bash +CONFIGURATION="test_namestore_api.conf" +trap "gnunet-arm -e -c $CONFIGURATION" SIGINT + +LOCATION=$(which gnunet-config) +if [ -z $LOCATION ] +then + LOCATION="gnunet-config" +fi +$LOCATION --version 1> /dev/null +if test $? != 0 +then + echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" + exit 77 +fi + +rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` +TEST_IP_PLUS="127.0.0.1" +TEST_RECORD_NAME_DNS="www3" +which timeout &> /dev/null && DO_TIMEOUT="timeout 5" + +# start peer +gnunet-arm -s -c $CONFIGURATION +gnunet-identity -C testego -c $CONFIGURATION + +# Create a public record +gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION +NAMESTORE_RES=$? +# Lookup specific name +OUTPUT=`gnunet-namestore -p -z testego -n $TEST_RECORD_NAME_DNS -D` + + +FOUND_IP=false +FOUND_NAME=false +for LINE in $OUTPUT ; + do + if echo "$LINE" | grep -q "$TEST_RECORD_NAME_DNS"; then + FOUND_NAME=true; + #echo $FOUND_NAME + fi + if echo "$LINE" | grep -q "$TEST_IP_PLUS"; then + FOUND_IP=true; + #echo $FOUND_IP + fi +done +# stop peer +gnunet-identity -D testego -c $CONFIGURATION +gnunet-arm -e -c $CONFIGURATION + + +if [ $FOUND_NAME = true -a $FOUND_IP = true ] +then + echo "PASS: Lookup name in namestore" + exit 0 +elif [ $FOUND_NAME = false ] +then + echo "FAIL: Lookup name in namestore: name not returned" + exit 1 +elif [ $FOUND_IP = false ] +then + echo "FAIL: Lookup name in namestore: IP not returned" + exit 1 +fi diff --git a/src/cli/namestore/test_namestore_put.sh b/src/cli/namestore/test_namestore_put.sh new file mode 100755 index 000000000..eaf7d44b4 --- /dev/null +++ b/src/cli/namestore/test_namestore_put.sh @@ -0,0 +1,55 @@ +#!/bin/bash +CONFIGURATION="test_namestore_api.conf" +trap "gnunet-arm -e -c $CONFIGURATION" SIGINT + +LOCATION=$(which gnunet-config) +if [ -z $LOCATION ] +then + LOCATION="gnunet-config" +fi +$LOCATION --version 1> /dev/null +if test $? != 0 +then + echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" + exit 77 +fi + +rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` +TEST_DOMAIN_PLUS="www.gnu" +TEST_DOMAIN_DNS="www3.gnu" +TEST_IP_PLUS="127.0.0.1" +TEST_IP_DNS="131.159.74.67" +TEST_RECORD_CNAME_SERVER="server" +TEST_RECORD_CNAME_PLUS="server.+" +TEST_RECORD_CNAME_DNS="gnunet.org" +TEST_RECORD_NAME_SERVER="server" +TEST_RECORD_NAME_PLUS="www" +TEST_RECORD_NAME_DNS="www3" +which timeout &> /dev/null && DO_TIMEOUT="timeout 5" + +function start_peer +{ + gnunet-arm -s -c $CONFIGURATION + gnunet-identity -C testego -c $CONFIGURATION +} + +function stop_peer +{ + gnunet-identity -D testego -c $CONFIGURATION + gnunet-arm -e -c $CONFIGURATION +} + + +start_peer +# Create a public record +gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION +NAMESTORE_RES=$? +stop_peer + +if [ $NAMESTORE_RES = 0 ] +then + echo "PASS: Creating name in namestore" +else + echo "FAIL: Creating name in namestore failed with $NAMESTORE_RES." + exit 1 +fi diff --git a/src/cli/namestore/test_namestore_put_multiple.sh b/src/cli/namestore/test_namestore_put_multiple.sh new file mode 100755 index 000000000..4c7340440 --- /dev/null +++ b/src/cli/namestore/test_namestore_put_multiple.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +# Check for required packages +if ! [ -x "$(command -v gnunet-namestore)" ]; then + echo 'bind/named is not installed' >&2 + exit 1 +fi + +# Check if gnunet is running +gnunet-arm -I 2&>1 /dev/null +ret=$? +if [ 0 -ne $ret ]; then + echo 'gnunet services are not running' + exit 1 +fi + +## GNUNET part +# Check if identity exists and deletes and readds it to get rid of entries in zone +gnunet-identity -d | grep randomtestingid 2>&1 /dev/null +ret=$? + +if [ 0 -ne $ret ]; then + gnunet-identity -D randomtestingid + gnunet-identity -C randomtestingid +fi + +function get_record_type { + arr=$1 + typ=$(echo -n "${arr[0]}" | cut -d' ' -f1) + echo "$typ" +} + +function get_value { + arr=$1 + val=$(echo -n "${arr[0]}" | cut -d' ' -f4-) + echo "$val" +} + +function testing { + label=$1 + records=$2 + recordstring="" + typ=$(get_record_type "${records[@]}") + for i in "${records[@]}" + do + recordstring+="$i"$'\n' + done + echo "$recordstring" + gnunet-namestore -a -S <&1 /dev/null + if [ 0 -ne $ret ]; then + echo "record $label could not be found" + fi +} + +# TEST CASES +# 1 +echo "Testing adding of single A record with -R" +declare -a arr=('A 1200 [r] 127.0.0.1') +testing test1 "${arr[@]}" +# 2 +echo "Testing adding of multiple A records with -R" +declare -a arr=('A 1200 [r] 127.0.0.1' 'A 2400 [r] 127.0.0.2') +testing test2 "${arr[@]}" +# 3 +echo "Testing adding of multiple different records with -R" +declare -a arr=('A 1200 [r] 127.0.0.1' 'AAAA 2400 [r] 2002::') +testing test3 "${arr[@]}" +# 4 +echo "Testing adding of single GNS2DNS record with -R" +declare -a arr=('GNS2DNS 86400 [r] gnu.org@127.0.0.1') +testing test4 "${arr[@]}" +# 5 +echo "Testing adding of single GNS2DNS shadow record with -R" +declare -a arr=('GNS2DNS 86409 [rs] gnu.org@127.0.0.250') +testing test5 "${arr[@]}" +# 6 +echo "Testing adding of multiple GNS2DNS record with -R" +declare -a arr=('GNS2DNS 1 [r] gnunet.org@127.0.0.1' 'GNS2DNS 3600 [s] gnunet.org@127.0.0.2') +testing test6 "${arr[@]}" +val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid) +if [[ $val == *"127.0.0.1"* ]]; then + echo "shadow!" +fi +echo "Sleeping to let record expire" +sleep 5 +val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid) +if [[ $val == *"127.0.0.2"* ]]; then + echo "no shadow!" +fi +# 7 +echo "Testing adding MX record with -R" +declare -a arr=('MX 3600 [r] 10,mail') +testing test7 "${arr[@]}" +# 8 +echo "Testing adding TXT record with -R" +declare -a arr=('TXT 3600 [r] Pretty_Unicorns') +testing test8 "${arr[@]}" +# 8 +#echo "Testing adding TXT record with -R" +#declare -a arr=('SRV 3600 [r] _autodiscover_old._tcp.bfh.ch.') +#testing test8 "${arr[@]}" + +# CLEANUP +gnunet-identity -D randomtestingid diff --git a/src/cli/namestore/test_namestore_put_stdin.sh b/src/cli/namestore/test_namestore_put_stdin.sh new file mode 100755 index 000000000..deb7bc4cb --- /dev/null +++ b/src/cli/namestore/test_namestore_put_stdin.sh @@ -0,0 +1,68 @@ +#!/bin/bash +CONFIGURATION="test_namestore_api.conf" +trap "gnunet-arm -e -c $CONFIGURATION" SIGINT + +LOCATION=$(which gnunet-config) +if [ -z $LOCATION ] +then + LOCATION="gnunet-config" +fi +$LOCATION --version 1> /dev/null +if test $? != 0 +then + echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" + exit 77 +fi + +rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` +TEST_RECORD_NAME="www3" +TEST_RECORD_NAME2="www" +TEST_IP="8.7.6.5" +TEST_IP2="1.2.3.4" + +which timeout &> /dev/null && DO_TIMEOUT="timeout 5" + +function start_peer +{ + gnunet-arm -s -c $CONFIGURATION + gnunet-identity -C testego -c $CONFIGURATION + gnunet-identity -C testego2 -c $CONFIGURATION +} + +function stop_peer +{ + gnunet-identity -D testego -c $CONFIGURATION + gnunet-identity -D testego2 -c $CONFIGURATION + gnunet-arm -e -c $CONFIGURATION +} + + +start_peer +# Create a public record +EGOKEY=`gnunet-identity -d | grep testego2 | cut -d' ' -f3` +gnunet-namestore -a -c $CONFIGURATION -S < /dev/null +if test $? != 0 +then + echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" + exit 77 +fi + +rm -rf `gnunet-config -c test_namestore_api.conf -f -s paths -o GNUNET_TEST_HOME` +which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" + +MY_EGO="myego" +gnunet-arm -s -c test_namestore_api.conf +gnunet-identity -C $MY_EGO -c test_namestore_api.conf +gnunet-namestore-zonefile -c test_namestore_api.conf < example_zonefile +res=$? +gnunet-identity -D $MY_EGO -c test_namestore_api.conf +gnunet-arm -e -c test_namestore_api.conf + +if [ $res != 0 ]; then + echo "FAIL: Zone import failed." + exit 1 +fi + + diff --git a/src/contrib/service/abd/Makefile.am b/src/contrib/service/abd/Makefile.am index 6eff980f4..d08a6a4a7 100644 --- a/src/contrib/service/abd/Makefile.am +++ b/src/contrib/service/abd/Makefile.am @@ -44,7 +44,7 @@ gnunet_abd_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(GN_LIBINTL) @@ -66,7 +66,7 @@ gnunet_service_abd_LDADD = \ libgnunetabd.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(top_builddir)/src/gns/libgnunetgns.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(GN_LIBINTL) diff --git a/src/conversation/Makefile.am b/src/conversation/Makefile.am index 4fca1ee79..720d543ca 100644 --- a/src/conversation/Makefile.am +++ b/src/conversation/Makefile.am @@ -71,7 +71,7 @@ libgnunetconversation_la_SOURCES = \ libgnunetconversation_la_LIBADD = \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -198,7 +198,7 @@ gnunet_conversation_LDADD = \ libgnunetconversation.la \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ $(INTLLIBS) @@ -223,7 +223,7 @@ test_conversation_api_LDADD = \ libgnunetspeaker.la \ libgnunetmicrophone.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -237,7 +237,7 @@ test_conversation_api_twocalls_LDADD = \ libgnunetspeaker.la \ libgnunetmicrophone.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la @@ -251,7 +251,7 @@ test_conversation_api_reject_LDADD = \ libgnunetspeaker.la \ libgnunetmicrophone.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/service/testing/libgnunettesting.la \ $(top_builddir)/src/lib/util/libgnunetutil.la diff --git a/src/gns/Makefile.am b/src/gns/Makefile.am index 379d6697a..08d85c903 100644 --- a/src/gns/Makefile.am +++ b/src/gns/Makefile.am @@ -186,7 +186,7 @@ test_gns_proxy_CFLAGS = $(MHD_CFLAGS) @LIBCURL_CPPFLAGS@ $(AM_CFLAGS) # gnunet-gns-import.c #gnunet_gns_import_LDADD = \ # $(top_builddir)/src/service/identity/libgnunetidentity.la \ -# $(top_builddir)/src/namestore/libgnunetnamestore.la \ +# $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ # $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ # $(top_builddir)/src/lib/util/libgnunetutil.la \ # $(GN_LIBINTL) diff --git a/src/namestore/.gitignore b/src/namestore/.gitignore deleted file mode 100644 index 2265f9815..000000000 --- a/src/namestore/.gitignore +++ /dev/null @@ -1,61 +0,0 @@ -gnunet-service-namestore -gnunet-namestore -gnunet-namestore-dbtool -gnunet-namestore-zonefile -gnunet-namestore-fcfsd -test_namestore_api_lookup_nick.nc -test_namestore_api_lookup_private.nc -test_namestore_api_lookup_public.nc -test_namestore_api_lookup_shadow.nc -test_namestore_api_lookup_shadow_filter.nc -test_namestore_api_monitoring.nc -test_namestore_api_monitoring_existing.nc -test_namestore_api_remove.nc -test_namestore_api_remove_not_existing_record.nc -test_namestore_api_store.nc -test_namestore_api_store_update.nc -test_namestore_api_zone_iteration.nc -test_namestore_api_zone_iteration_nick.nc -test_namestore_api_zone_iteration_specific_zone.nc -test_namestore_api_zone_iteration_stop.nc -test_plugin_namestore_postgres -test_plugin_namestore_sqlite -gnunet-zoneimport -test_namestore_api_lookup_nick_postgres -test_namestore_api_lookup_nick_sqlite -test_namestore_api_lookup_private_postgres -test_namestore_api_lookup_private_sqlite -test_namestore_api_lookup_public_postgres -test_namestore_api_lookup_public_sqlite -test_namestore_api_lookup_shadow_filter_postgres -test_namestore_api_lookup_shadow_filter_sqlite -test_namestore_api_lookup_shadow_postgres -test_namestore_api_lookup_shadow_sqlite -test_namestore_api_monitoring_existing_postgres -test_namestore_api_monitoring_existing_sqlite -test_namestore_api_monitoring_postgres -test_namestore_api_monitoring_sqlite -test_namestore_api_remove_not_existing_record_postgres -test_namestore_api_remove_not_existing_record_sqlite -test_namestore_api_remove_postgres -test_namestore_api_remove_sqlite -test_namestore_api_store_postgres -test_namestore_api_store_sqlite -test_namestore_api_store_update_postgres -test_namestore_api_store_update_sqlite -test_namestore_api_zone_iteration_nick_postgres -test_namestore_api_zone_iteration_nick_sqlite -test_namestore_api_zone_iteration_postgres -test_namestore_api_zone_iteration_specific_zone_postgres -test_namestore_api_zone_iteration_specific_zone_sqlite -test_namestore_api_zone_iteration_sqlite -test_namestore_api_zone_iteration_stop_postgres -test_namestore_api_zone_iteration_stop_sqlite -test_namestore_api_zone_to_name_postgres -test_namestore_api_zone_to_name_sqlite -test_namestore_api_tx_rollback_postgres -test_namestore_api_tx_rollback_sqlite -test_namestore_api_edit_records_postgres -perf_namestore_api_import_sqlite -perf_namestore_api_import_postgres -test_namestore_api_store_locking_sqlite diff --git a/src/namestore/Makefile.am b/src/namestore/Makefile.am deleted file mode 100644 index 685829cc2..000000000 --- a/src/namestore/Makefile.am +++ /dev/null @@ -1,543 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS) - -plugindir = $(libdir)/gnunet - -pkgcfgdir= $(pkgdatadir)/config.d/ - -libexecdir= $(pkglibdir)/libexec/ - -sqldir = $(prefix)/share/gnunet/sql/ - -sql_DATA = \ - namestore-0001.sql \ - namestore-drop.sql - -pkgcfg_DATA = \ - namestore.conf - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIBS = -lgcov -endif - - -if HAVE_SQLITE -SQLITE_PLUGIN = libgnunet_plugin_namestore_sqlite.la -SQLITE_TESTS = test_plugin_namestore_sqlite \ - test_namestore_api_store_sqlite \ - test_namestore_api_store_update_sqlite \ - test_namestore_api_zone_iteration_sqlite \ - test_namestore_api_remove_sqlite \ - test_namestore_api_lookup_nick_sqlite \ - test_namestore_api_monitoring_sqlite \ - test_namestore_api_remove_not_existing_record_sqlite \ - test_namestore_api_zone_iteration_nick_sqlite \ - test_namestore_api_zone_iteration_specific_zone_sqlite \ - test_namestore_api_zone_iteration_stop_sqlite \ - test_namestore_api_monitoring_existing_sqlite \ - test_namestore_api_zone_to_name_sqlite \ - perf_namestore_api_zone_iteration_sqlite \ - perf_namestore_api_import_sqlite \ - perf_namestore_api_import_postgres \ - test_namestore_api_tx_rollback_sqlite -endif - - -if HAVE_POSTGRESQL -POSTGRES_PLUGIN = libgnunet_plugin_namestore_postgres.la -POSTGRES_TESTS = test_plugin_namestore_postgres \ - test_namestore_api_store_postgres \ - test_namestore_api_store_update_postgres \ - test_namestore_api_remove_postgres \ - test_namestore_api_zone_iteration_postgres \ - test_namestore_api_lookup_nick_postgres \ - test_namestore_api_monitoring_postgres \ - test_namestore_api_remove_not_existing_record_postgres \ - test_namestore_api_zone_iteration_nick_postgres \ - test_namestore_api_zone_iteration_specific_zone_postgres \ - test_namestore_api_zone_iteration_stop_postgres \ - test_namestore_api_monitoring_existing_postgres \ - test_namestore_api_zone_to_name_postgres \ - perf_namestore_api_zone_iteration_postgres \ - test_namestore_api_tx_rollback_postgres -if HAVE_EXPERIMENTAL -POSTGRES_TESTS += test_namestore_api_edit_records_postgres -endif -endif - -if HAVE_SQLITE -check_PROGRAMS = \ - $(SQLITE_TESTS) \ - $(POSTGRES_TESTS) -endif - -if ENABLE_TEST_RUN -AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; -TESTS = \ - $(check_PROGRAMS) \ - $(check_SCRIPTS) -endif - -REST_PLUGIN = libgnunet_plugin_rest_namestore.la - -lib_LTLIBRARIES = \ - libgnunetnamestore.la - - -libexec_PROGRAMS = \ - gnunet-service-namestore - -bin_PROGRAMS = \ - gnunet-namestore \ - gnunet-namestore-dbtool \ - gnunet-namestore-zonefile \ - gnunet-zoneimport - -libexec_PROGRAMS += \ - gnunet-namestore-fcfsd - - -plugin_LTLIBRARIES = \ - $(SQLITE_PLUGIN) \ - $(POSTGRES_PLUGIN) \ - $(REST_PLUGIN) - - -libgnunet_plugin_rest_namestore_la_SOURCES = \ - plugin_rest_namestore.c -libgnunet_plugin_rest_namestore_la_LIBADD = \ - libgnunetnamestore.la \ - $(top_builddir)/src/service/rest/libgnunetrest.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/json/libgnunetjson.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecordjson.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ - $(LTLIBINTL) -ljansson $(MHD_LIBS) -libgnunet_plugin_rest_namestore_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) -libgnunet_plugin_rest_namestore_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) - - -libgnunetnamestore_la_SOURCES = \ - namestore_api.c \ - namestore_api_monitor.c \ - namestore.h -libgnunetnamestore_la_LIBADD = \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) -libgnunetnamestore_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) \ - -version-info 0:1:0 - -gnunet_namestore_zonefile_SOURCES = \ - gnunet-namestore-zonefile.c -gnunet_namestore_zonefile_LDADD = \ - libgnunetnamestore.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) - -gnunet_zoneimport_SOURCES = \ - gnunet-zoneimport.c -gnunet_zoneimport_LDADD = \ - libgnunetnamestore.la \ - $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(GN_LIBINTL) - -gnunet_namestore_SOURCES = \ - gnunet-namestore.c -gnunet_namestore_LDADD = \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - libgnunetnamestore.la \ - $(GN_LIBINTL) - -gnunet_namestore_dbtool_SOURCES = \ - gnunet-namestore-dbtool.c -gnunet_namestore_dbtool_LDADD = \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - libgnunetnamestore.la \ - $(GN_LIBINTL) - - - -gnunet_namestore_fcfsd_SOURCES = \ - gnunet-namestore-fcfsd.c -gnunet_namestore_fcfsd_LDADD = $(MHD_LIBS) \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/json/libgnunetjson.la \ - $(GN_LIBINTL) -ljansson -gnunet_namestore_fcfsd_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) - - -gnunet_service_namestore_SOURCES = \ - gnunet-service-namestore.c -gnunet_service_namestore_LDADD = \ - $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - libgnunetnamestore.la \ - $(GN_LIBINTL) - - - -libgnunet_plugin_namestore_sqlite_la_SOURCES = \ - plugin_namestore_sqlite.c -libgnunet_plugin_namestore_sqlite_la_LIBADD = \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/sq/libgnunetsq.la \ - $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ - $(LTLIBINTL) -libgnunet_plugin_namestore_sqlite_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) - -libgnunet_plugin_namestore_postgres_la_SOURCES = \ - plugin_namestore_postgres.c -libgnunet_plugin_namestore_postgres_la_LIBADD = \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/pq/libgnunetpq.la \ - $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \ - $(LTLIBINTL) -libgnunet_plugin_namestore_postgres_la_LDFLAGS = \ - $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS) - -test_namestore_api_store_sqlite_SOURCES = \ - test_namestore_api_store.c -test_namestore_api_store_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la - -test_namestore_api_store_postgres_SOURCES = \ - test_namestore_api_store.c -test_namestore_api_store_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la - -test_namestore_api_store_update_sqlite_SOURCES = \ - test_namestore_api_store_update.c -test_namestore_api_store_update_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ - libgnunetnamestore.la - -test_namestore_api_store_update_postgres_SOURCES = \ - test_namestore_api_store_update.c -test_namestore_api_store_update_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ - libgnunetnamestore.la - -test_namestore_api_lookup_nick_sqlite_SOURCES = \ - test_namestore_api_lookup_nick.c -test_namestore_api_lookup_nick_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ - libgnunetnamestore.la - -test_namestore_api_lookup_nick_postgres_SOURCES = \ - test_namestore_api_lookup_nick.c -test_namestore_api_lookup_nick_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ - libgnunetnamestore.la - -test_namestore_api_remove_sqlite_SOURCES = \ - test_namestore_api_remove.c -test_namestore_api_remove_sqlite_LDADD = \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_remove_postgres_SOURCES = \ - test_namestore_api_remove.c -test_namestore_api_remove_postgres_LDADD = \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_remove_not_existing_record_sqlite_SOURCES = \ - test_namestore_api_remove_not_existing_record.c -test_namestore_api_remove_not_existing_record_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_remove_not_existing_record_postgres_SOURCES = \ - test_namestore_api_remove_not_existing_record.c -test_namestore_api_remove_not_existing_record_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_to_name_sqlite_SOURCES = \ - test_namestore_api_zone_to_name.c -test_namestore_api_zone_to_name_sqlite_LDADD = \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - libgnunetnamestore.la - -test_namestore_api_zone_to_name_postgres_SOURCES = \ - test_namestore_api_zone_to_name.c -test_namestore_api_zone_to_name_postgres_LDADD = \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - libgnunetnamestore.la - -test_namestore_api_monitoring_sqlite_SOURCES = \ - test_namestore_api_monitoring.c -test_namestore_api_monitoring_sqlite_LDADD = \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_namestore_api_monitoring_postgres_SOURCES = \ - test_namestore_api_monitoring.c -test_namestore_api_monitoring_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_namestore_api_monitoring_existing_sqlite_SOURCES = \ - test_namestore_api_monitoring_existing.c -test_namestore_api_monitoring_existing_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_namestore_api_monitoring_existing_postgres_SOURCES = \ - test_namestore_api_monitoring_existing.c -test_namestore_api_monitoring_existing_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_namestore_api_tx_rollback_sqlite_SOURCES = \ - test_namestore_api_tx_rollback.c -test_namestore_api_tx_rollback_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_namestore_api_tx_rollback_postgres_SOURCES = \ - test_namestore_api_tx_rollback.c -test_namestore_api_tx_rollback_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -if HAVE_EXPERIMENTAL -test_namestore_api_edit_records_postgres_SOURCES = \ - test_namestore_api_edit_records.c -test_namestore_api_edit_records_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - libgnunetnamestore.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la -endif - -test_namestore_api_zone_iteration_sqlite_SOURCES = \ - test_namestore_api_zone_iteration.c -test_namestore_api_zone_iteration_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_iteration_postgres_SOURCES = \ - test_namestore_api_zone_iteration.c -test_namestore_api_zone_iteration_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -perf_namestore_api_zone_iteration_postgres_SOURCES = \ - perf_namestore_api_zone_iteration.c -perf_namestore_api_zone_iteration_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -perf_namestore_api_import_sqlite_SOURCES = \ - perf_namestore_api_import.c -perf_namestore_api_import_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -perf_namestore_api_import_postgres_SOURCES = \ - perf_namestore_api_import.c -perf_namestore_api_import_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - - -perf_namestore_api_zone_iteration_sqlite_SOURCES = \ - perf_namestore_api_zone_iteration.c -perf_namestore_api_zone_iteration_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_iteration_nick_sqlite_SOURCES = \ - test_namestore_api_zone_iteration_nick.c -test_namestore_api_zone_iteration_nick_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_iteration_nick_postgres_SOURCES = \ - test_namestore_api_zone_iteration_nick.c -test_namestore_api_zone_iteration_nick_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_iteration_specific_zone_sqlite_SOURCES = \ - test_namestore_api_zone_iteration_specific_zone.c -test_namestore_api_zone_iteration_specific_zone_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_iteration_specific_zone_postgres_SOURCES = \ - test_namestore_api_zone_iteration_specific_zone.c -test_namestore_api_zone_iteration_specific_zone_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_iteration_stop_sqlite_SOURCES = \ - test_namestore_api_zone_iteration_stop.c -test_namestore_api_zone_iteration_stop_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_namestore_api_zone_iteration_stop_postgres_SOURCES = \ - test_namestore_api_zone_iteration_stop.c -test_namestore_api_zone_iteration_stop_postgres_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ - libgnunetnamestore.la - -test_plugin_namestore_sqlite_SOURCES = \ - test_plugin_namestore.c -test_plugin_namestore_sqlite_LDADD = \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -test_plugin_namestore_postgres_SOURCES = \ - test_plugin_namestore.c -test_plugin_namestore_postgres_LDADD = \ - $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/service/testing/libgnunettesting.la \ - $(top_builddir)/src/lib/util/libgnunetutil.la - -check_SCRIPTS = \ - test_namestore_put.sh \ - test_namestore_put_stdin.sh \ - test_namestore_lookup.sh \ - test_namestore_delete.sh \ - test_namestore_zonefile_import.sh - -check_SCRIPTS += \ - test_plugin_rest_namestore.sh - -EXTRA_DIST = \ - test_common.c \ - test_namestore_api.conf \ - test_namestore_api_postgres.conf \ - test_namestore_api_sqlite.conf \ - perf_namestore_api_postgres.conf \ - perf_namestore_api_sqlite.conf \ - test_plugin_namestore_sqlite.conf \ - test_plugin_namestore_postgres.conf \ - test_hostkey \ - example_zonefile \ - $(check_SCRIPTS) \ - $(sql_DATA) diff --git a/src/namestore/example_zonefile b/src/namestore/example_zonefile deleted file mode 100644 index 5e380ff90..000000000 --- a/src/namestore/example_zonefile +++ /dev/null @@ -1,27 +0,0 @@ -$ORIGIN example.com. ; designates the start of this zone file in the namespace -$TTL 3600 ; default expiration time (in seconds) of all RRs without their own TTL value -example.com. IN SOA ns.example.com. username.example.com. ( 2020091025 ; A comment - 7200 ; Comment - ; empty line on purpose - 3600 - 1209600 - 3600 ) -example.com. IN NS ns ; ns.example.com is a nameserver for example.com -example.com. IN NS ns.somewhere.example. ; ns.somewhere.example is a backup nameserver for example.com -example.com. IN MX 10 mail.example.com. ; mail.example.com is the mailserver for example.com -@ IN MX 20 mail2.example.com. ; equivalent to above line, "@" represents zone origin -@ IN MX 50 mail3 ; equivalent to above line, but using a relative host name -b.example.com. IN A 192.0.2.1 ; IPv4 address for example.com - IN AAAA 2001:db8:10::1 ; IPv6 address for example.com -ns IN A 192.0.2.2 ; IPv4 address for ns.example.com - IN AAAA 2001:db8:10::2 ; IPv6 address for ns.example.com -www IN CNAME example.com. ; www.example.com is an alias for example.com -wwwtest IN CNAME www ; wwwtest.example.com is another alias for www.example.com -mail IN A 192.0.2.3 ; IPv4 address for mail.example.com -mail2 IN A 192.0.2.4 ; IPv4 address for mail2.example.com -mail3 IN A 192.0.2.5 ; IPv4 address for mail3.example.com - -mail3 IN TXT "This is ; quoted" ; A quoted comment separator -$ORIGIN example.de. -www2 IN A 192.0.2.7 ; IPv4 address for www2.example.de - diff --git a/src/namestore/gnunet-namestore-dbtool.c b/src/namestore/gnunet-namestore-dbtool.c deleted file mode 100644 index 835d7a228..000000000 --- a/src/namestore/gnunet-namestore-dbtool.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012, 2013, 2014, 2019, 2022 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file gnunet-namestore-dbtool.c - * @brief command line tool to manipulate the database backends for the namestore - * @author Martin Schanzenbach - * - */ -#include "platform.h" -#include -#include - -/** - * Name of the plugin argument - */ -static char *pluginname; - -/** - * Reset argument - */ -static int reset; - -/** - * Initialize argument - */ -static int init; - -/** - * Return code - */ -static int ret = 0; - -/** - * Task run on shutdown. Cleans up everything. - * - * @param cls unused - */ -static void -do_shutdown (void *cls) -{ - (void) cls; - if (NULL != pluginname) - GNUNET_free (pluginname); -} - - -/** - * Main function that will be run. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param cfg configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - char *db_lib_name; - struct GNUNET_NAMESTORE_PluginFunctions *plugin; - - (void) cls; - (void) args; - (void) cfgfile; - if (NULL != args[0]) - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - _ ("Superfluous command line arguments (starting with `%s') ignored\n"), - args[0]); - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - (void *) cfg); - if (NULL == pluginname) - { - fprintf (stderr, "No plugin given!\n"); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_asprintf (&db_lib_name, - "libgnunet_plugin_namestore_%s", - pluginname); - plugin = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); - if (NULL == plugin) - { - fprintf (stderr, - "Failed to load %s!\n", - db_lib_name); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (db_lib_name); - return; - } - if (reset) - { - if (GNUNET_OK != - plugin->drop_tables (plugin->cls)) - { - fprintf (stderr, - "Failed to reset database\n"); - ret = 1; - GNUNET_free (db_lib_name); - GNUNET_SCHEDULER_shutdown (); - return; - } - } - if (init || reset) - { - if (GNUNET_OK != - plugin->create_tables (plugin->cls)) - { - fprintf (stderr, - "Failed to initialize database\n"); - ret = 1; - GNUNET_free (db_lib_name); - GNUNET_SCHEDULER_shutdown (); - return; - } - } - GNUNET_SCHEDULER_shutdown (); - GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, - plugin)); - GNUNET_free (db_lib_name); -} - - -/** - * The main function for gnunet-namestore-dbtool. - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, char *const *argv) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_flag ('i', "init", - gettext_noop ("initialize database"), - &init), - GNUNET_GETOPT_option_flag ('r', - "reset", - gettext_noop ( - "reset database (DANGEROUS: All existing data is lost!"), - &reset), - GNUNET_GETOPT_option_string ( - 'p', - "plugin", - "PLUGIN", - gettext_noop ( - "the namestore plugin to work with, e.g. 'sqlite'"), - &pluginname), - GNUNET_GETOPT_OPTION_END - }; - int lret; - - if (GNUNET_OK != - GNUNET_STRINGS_get_utf8_args (argc, argv, - &argc, &argv)) - return 2; - - GNUNET_log_setup ("gnunet-namestore-dbtool", - "WARNING", - NULL); - if (GNUNET_OK != - (lret = GNUNET_PROGRAM_run (argc, - argv, - "gnunet-namestore-dbtool", - _ ( - "GNUnet namestore database manipulation tool"), - options, - &run, - NULL))) - { - GNUNET_free_nz ((void *) argv); - return lret; - } - GNUNET_free_nz ((void *) argv); - return ret; -} diff --git a/src/namestore/gnunet-namestore-fcfsd.c b/src/namestore/gnunet-namestore-fcfsd.c deleted file mode 100644 index 4948ae441..000000000 --- a/src/namestore/gnunet-namestore-fcfsd.c +++ /dev/null @@ -1,1160 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2021 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file gnunet-namestore-fcfsd.c - * @brief HTTP daemon that offers first-come-first-serve GNS domain registration - * @author Christian Grothoff - */ - -#include "platform.h" -#include -#include "gnunet_util_lib.h" -#include "gnunet_identity_service.h" -#include "gnunet_gnsrecord_lib.h" -#include "gnunet_namestore_service.h" -#include "gnunet_mhd_compat.h" -#include "gnunet_json_lib.h" - -/** - * Structure representing a static page. - * "Static" means that the server does not process the page before sending it - * to the client. Clients can still process the received data, for example - * because there are scripting elements within. - */ -struct StaticPage -{ - /** - * Handle to file on disk. - */ - struct GNUNET_DISK_FileHandle *handle; - - /** - * Size in bytes of the file. - */ - uint64_t size; - - /** - * Cached response object to send to clients. - */ - struct MHD_Response *response; -}; - -/** - * Structure containing some request-specific data. - */ -struct RequestData -{ - /** - * The connection this request was sent in. - */ - struct MHD_Connection *c; - - /** - * Body of the response object. - */ - char *body; - - /** - * Length in bytes of the body. - */ - size_t body_length; - - /** - * Response code. - */ - int code; - - /** - * Task started to search for an entry in the namestore. - */ - struct GNUNET_NAMESTORE_QueueEntry *searching; - - /** - * Task started to iterate over the namestore. - */ - struct GNUNET_NAMESTORE_ZoneIterator *iterating; - - /** - * Pointer used while processing POST data. - */ - void *ptr; - - /** - * Name requested to be registered. - */ - char *register_name; - - /** - * Key (encoded as a string) to be associated with the requested name. - */ - char *register_key; - - /** - * Key to be associated with the requested name. - */ - struct GNUNET_CRYPTO_PublicKey key; -}; - -/** - * Name of the zone being managed. - */ -static char *zone = NULL; - -/** - * The port the daemon is listening to for HTTP requests. - */ -static unsigned long long port = 18080; - -/** - * Connection with the namestore service. - */ -static struct GNUNET_NAMESTORE_Handle *namestore = NULL; - -/** - * Connection with the identity service. - */ -static struct GNUNET_IDENTITY_Handle *identity = NULL; - -/** - * Private key of the zone. - */ -static const struct GNUNET_CRYPTO_PrivateKey *zone_key = NULL; - -/** - * The HTTP daemon. - */ -static struct MHD_Daemon *httpd = NULL; - -/** - * Task executing the HTTP daemon. - */ -static struct GNUNET_SCHEDULER_Task *httpd_task = NULL; - -/** - * The main page, a.k.a. "index.html" - */ -static struct StaticPage *main_page = NULL; - -/** - * Page indicating the requested resource could not be found. - */ -static struct StaticPage *notfound_page = NULL; - -/** - * Page indicating the requested resource could not be accessed, and other - * errors. - */ -static struct StaticPage *forbidden_page = NULL; - -/** - * The relative expiration time for added records - */ -static struct GNUNET_TIME_Relative record_exp; - -/** - * Task ran at shutdown to clean up everything. - * - * @param cls unused - */ -static void -do_shutdown (void *cls) -{ - /* We cheat a bit here: the file descriptor is implicitly closed by MHD, so - calling `GNUNET_DISK_file_close' would generate a spurious warning message - in the log. Since that function does nothing but close the descriptor and - free the allocated memory, After destroying the response all that's left to - do is call `GNUNET_free'. */ - if (NULL != main_page) - { - MHD_destroy_response (main_page->response); - GNUNET_free (main_page->handle); - GNUNET_free (main_page); - } - if (NULL != notfound_page) - { - MHD_destroy_response (notfound_page->response); - GNUNET_free (notfound_page->handle); - GNUNET_free (notfound_page); - } - if (NULL != forbidden_page) - { - MHD_destroy_response (forbidden_page->response); - GNUNET_free (forbidden_page->handle); - GNUNET_free (forbidden_page); - } - - if (NULL != namestore) - { - GNUNET_NAMESTORE_disconnect (namestore); - } - - if (NULL != identity) - { - GNUNET_IDENTITY_disconnect (identity); - } - - if (NULL != httpd_task) - { - GNUNET_SCHEDULER_cancel (httpd_task); - } - if (NULL != httpd) - { - MHD_stop_daemon (httpd); - } -} - - -/** - * Called when the HTTP server has some pending operations. - * - * @param cls unused - */ -static void -do_httpd (void *cls); - -/** - * Schedule a task to run MHD. - */ -static void -run_httpd (void) -{ - fd_set rs; - fd_set ws; - fd_set es; - - struct GNUNET_NETWORK_FDSet *grs = GNUNET_NETWORK_fdset_create (); - struct GNUNET_NETWORK_FDSet *gws = GNUNET_NETWORK_fdset_create (); - struct GNUNET_NETWORK_FDSet *ges = GNUNET_NETWORK_fdset_create (); - - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - - int max = -1; - GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max)); - - unsigned MHD_LONG_LONG timeout = 0; - struct GNUNET_TIME_Relative gtime = GNUNET_TIME_UNIT_FOREVER_REL; - if (MHD_YES == MHD_get_timeout (httpd, &timeout)) - { - gtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, - timeout); - } - - GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); - GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); - GNUNET_NETWORK_fdset_copy_native (ges, &es, max + 1); - - httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, - gtime, - grs, - gws, - &do_httpd, - NULL); - GNUNET_NETWORK_fdset_destroy (grs); - GNUNET_NETWORK_fdset_destroy (gws); - GNUNET_NETWORK_fdset_destroy (ges); -} - - -/** - * Called when the HTTP server has some pending operations. - * - * @param cls unused - */ -static void -do_httpd (void *cls) -{ - httpd_task = NULL; - MHD_run (httpd); - run_httpd (); -} - - -static void -run_httpd_now (void) -{ - if (NULL != httpd_task) - { - GNUNET_SCHEDULER_cancel (httpd_task); - httpd_task = NULL; - } - httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL); -} - - -/** - * Generate a JSON object. - * - * @param key the key for the first element - * @param value the value for the first element - * @param ... key-value pairs of the object, terminated by NULL - * @return a JSON string (allocated) - */ -static char * -make_json (const char *key, const char *value, ...) -{ - va_list args; - va_start (args, value); - - json_t *obj = NULL; - - obj = json_object (); - if ((NULL == key) || (NULL == value)) - { - va_end (args); - return json_dumps (obj, JSON_COMPACT); - } - - json_object_set (obj, key, json_string (value)); - - char *k = va_arg (args, char *); - if (NULL == k) - { - va_end (args); - return json_dumps (obj, JSON_COMPACT); - } - char *v = va_arg (args, char *); - if (NULL == v) - { - va_end (args); - return json_dumps (obj, JSON_COMPACT); - } - - while (NULL != k && NULL != v) - { - json_object_set (obj, k, json_string (v)); - k = va_arg (args, char *); - if (NULL != k) - { - v = va_arg (args, char *); - } - } - - va_end (args); - - char *json = json_dumps (obj, JSON_COMPACT); - json_decref (obj); - - return json; -} - - -/** - * The namestore search task failed. - * - * @param cls the request data - */ -static void -search_error_cb (void *cls) -{ - struct RequestData *rd = cls; - MHD_resume_connection (rd->c); - rd->searching = NULL; - rd->body = make_json ("error", "true", - "message", _ ("can not search the namestore"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; - run_httpd_now (); -} - - -/** - * The lookup terminated with some results. - * - * @param cls closure - * @param zone the private key of the zone - * @param label the result label - * @param count number of records found - * @param d records found - */ -static void -search_done_cb (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int count, - const struct GNUNET_GNSRECORD_Data *d) -{ - (void) zone; - (void) d; - - struct RequestData *rd = cls; - MHD_resume_connection (rd->c); - - rd->searching = NULL; - rd->body = make_json ("error", "false", - "free", (0 == count) ? "true" : "false", - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_OK; - - run_httpd_now (); -} - - -/** - * An error occurred while registering a name. - * - * @param cls the connection - */ -static void -register_error_cb (void *cls) -{ - struct RequestData *rd = cls; - - MHD_resume_connection (rd->c); - rd->searching = NULL; - rd->body = make_json ("error", "true", - "message", _ ("unable to scan namestore"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; - run_httpd_now (); -} - - -static void -register_done_cb (void *cls, - enum GNUNET_ErrorCode ec) -{ - struct RequestData *rd = cls; - - MHD_resume_connection (rd->c); - rd->searching = NULL; - - if (GNUNET_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ("Failed to create record for `%s': %s\n"), - rd->register_name, - GNUNET_ErrorCode_get_hint (ec)); - rd->body = make_json ("error", "true", - "message", - GNUNET_ErrorCode_get_hint (ec), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; - } - else - { - rd->body = make_json ("error", "false", - "message", _ ("no errors"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_OK; - } - - run_httpd_now (); -} - - -/** - * Attempt to register the requested name. - * - * @param cls the connection - * @param key the zone key - * @param label name of the record - * @param count number of records found - * @param d records - */ -static void -register_do_cb (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *key, - const char *label, - unsigned int count, - const struct GNUNET_GNSRECORD_Data *d) -{ - (void) key; - (void) d; - - struct RequestData *rd = cls; - - rd->searching = NULL; - - if (0 != count) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("The requested key `%s' exists as `%s'\n"), - rd->register_key, - label); - - MHD_resume_connection (rd->c); - rd->searching = NULL; - rd->body = make_json ("error", "true", - "message", _ ("key exists"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_FORBIDDEN; - run_httpd_now (); - return; - } - - struct GNUNET_GNSRECORD_Data gd; - char *gdraw = NULL; - - if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&(rd->key), - &gdraw, - &(gd.data_size), - &(gd.record_type))) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Error creating record data\n")); - MHD_resume_connection (rd->c); - rd->searching = NULL; - rd->body = make_json ("error", "true", - "message", _ ("unable to store record"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; - run_httpd_now (); - return; - } - - gd.data = gdraw; - gd.expiration_time = record_exp.rel_value_us; - gd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - - rd->searching = GNUNET_NAMESTORE_records_store (namestore, - zone_key, - rd->register_name, - 1, - &gd, - ®ister_done_cb, - rd); - - GNUNET_free (gdraw); -} - - -/** - * An error occurred while iterating the namestore. - * - * @param cls the connection - */ -static void -iterate_error_cb (void *cls) -{ - struct RequestData *rd = cls; - - MHD_resume_connection (rd->c); - rd->iterating = NULL; - rd->body = make_json ("error", "true", - "message", _ ("unable to scan namestore"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; - run_httpd_now (); -} - - -/** - * A block was received from the namestore. - * - * @param cls the connection - * @param key the zone key - * @param label the records' label - * @param count number of records found - * @param d the found records - */ -static void -iterate_do_cb (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *key, - const char *label, - unsigned int count, - const struct GNUNET_GNSRECORD_Data *d) -{ - (void) key; - (void) label; - (void) d; - - struct RequestData *rd = cls; - - if (0 == strcmp (label, rd->register_name)) - { - GNUNET_break (0 != count); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Requested name `%s' exists with `%u' records\n"), - rd->register_name, - count); - - MHD_resume_connection (rd->c); - rd->body = make_json ("error", "true", - "message", _ ("name exists\n"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_FORBIDDEN; - GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating); - run_httpd_now (); - return; - } - - GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1); -} - - -/** - * All entries in the namestore have been iterated over. - * - * @param cls the connection - */ -static void -iterate_done_cb (void *cls) -{ - struct RequestData *rd = cls; - - rd->iterating = NULL; - - /* See if the key was not registered already */ - rd->searching = GNUNET_NAMESTORE_zone_to_name (namestore, - zone_key, - &(rd->key), - ®ister_error_cb, - rd, - ®ister_do_cb, - rd); -} - - -/** - * Generate a response containing JSON and send it to the client. - * - * @param c the connection - * @param body the response body - * @param length the body length in bytes - * @param code the response code - * @return MHD_NO on error - */ -static MHD_RESULT -serve_json (struct MHD_Connection *c, - char *body, - size_t length, - int code) -{ - struct MHD_Response *response = - MHD_create_response_from_buffer (length, - body, - MHD_RESPMEM_PERSISTENT); - MHD_RESULT r = MHD_queue_response (c, code, response); - MHD_destroy_response (response); - return r; -} - - -/** - * Send a response back to a connected client. - * - * @param cls unused - * @param connection the connection with the client - * @param url the requested address - * @param method the HTTP method used - * @param version the protocol version (including the "HTTP/" part) - * @param upload_data data sent with a POST request - * @param upload_data_size length in bytes of the POST data - * @param ptr used to pass data between request handling phases - * @return MHD_NO on error - */ -static MHD_RESULT -create_response (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **ptr) -{ - (void) cls; - (void) version; - - struct RequestData *rd = *ptr; - - if (0 == strcmp (method, MHD_HTTP_METHOD_GET)) - { - /* Handle a previously suspended request */ - if (NULL != rd) - { - return serve_json (rd->c, rd->body, rd->body_length, rd->code); - } - - if (0 == strcmp ("/", url)) - { - return MHD_queue_response (connection, - MHD_HTTP_OK, - main_page->response); - } - - if (0 == strcmp ("/search", url)) - { - const char *name = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "name"); - if (NULL == name) - { - return MHD_queue_response (connection, - MHD_HTTP_BAD_REQUEST, - forbidden_page->response); - } - - MHD_suspend_connection (connection); - rd = GNUNET_new (struct RequestData); - rd->c = connection; - rd->searching = GNUNET_NAMESTORE_records_lookup (namestore, - zone_key, - name, - &search_error_cb, - rd, - &search_done_cb, - rd); - *ptr = rd; - return MHD_YES; - } - - return MHD_queue_response (connection, - MHD_HTTP_NOT_FOUND, - notfound_page->response); - } - - if (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) - { - /* We take a shortcut here by always serving the main page: starting a - namestore lookup, allocating the necessary resources, waiting for the - lookup to complete and then discard everything just because it was a HEAD - and thus only the headers are significative, is an unnecessary waste of - resources. The handling of this method could be smarter, for example by - sending a proper content type header based on the endpoint, but this is - not a service in which HEAD requests are significant, so there's no need - to spend too much time here. */ - return MHD_queue_response (connection, - MHD_HTTP_OK, - main_page->response); - } - - if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) - { - if (0 == strcmp ("/register", url)) - { - /* Handle a previously suspended request */ - if ((NULL != rd) && (NULL != rd->body)) - { - return serve_json (rd->c, rd->body, rd->body_length, rd->code); - } - - if (NULL == rd) - { - rd = GNUNET_new (struct RequestData); - rd->c = connection; - rd->body = NULL; - rd->ptr = NULL; - *ptr = rd; - } - - json_t *json = NULL; - enum GNUNET_JSON_PostResult result = - GNUNET_JSON_post_parser (32 * 1024, - connection, - &(rd->ptr), - upload_data, - upload_data_size, - &json); - - switch (result) - { - case GNUNET_JSON_PR_CONTINUE: - /* Keep processing POST data */ - return MHD_YES; - case GNUNET_JSON_PR_OUT_OF_MEMORY: - case GNUNET_JSON_PR_REQUEST_TOO_LARGE: - rd->body = make_json ("error", "true", - "message", _ ("unable to process submitted data"), - NULL); - rd->body_length = strlen (rd->body); -#ifdef MHD_HTTP_CONTENT_TOO_LARGE - rd->code = MHD_HTTP_CONTENT_TOO_LARGE; -#else - rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE; -#endif - return MHD_YES; - case GNUNET_JSON_PR_JSON_INVALID: - rd->body = make_json ("error", "true", - "message", _ ("the submitted data is invalid"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_BAD_REQUEST; - return MHD_YES; - default: - break; - } - - /* POST data has been read in its entirety */ - - const char *name = json_string_value (json_object_get (json, "name")); - const char *key = json_string_value (json_object_get (json, "key")); - if ((NULL == name) || (NULL == key) || (0 == strlen (name)) || (0 == - strlen ( - key))) - { - json_decref (json); - rd->body = make_json ("error", "true", - "message", _ ("invalid parameters"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_BAD_REQUEST; - return MHD_YES; - } - - rd->register_name = strdup (name); - rd->register_key = strdup (key); - - json_decref (json); - GNUNET_JSON_post_parser_cleanup (rd->ptr); - - if ((NULL != strchr (rd->register_name, '.')) || - (NULL != strchr (rd->register_name, '+'))) - { - rd->body = make_json ("error", "true", - "message", _ ("invalid name"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_BAD_REQUEST; - return MHD_YES; - } - - if (GNUNET_OK != GNUNET_CRYPTO_public_key_from_string (rd->register_key, - &(rd->key))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - _ ("Unable to parse key %s\n"), - rd->register_key); - - rd->body = make_json ("error", "true", - "message", _ ("unable to parse key"), - NULL); - rd->body_length = strlen (rd->body); - rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; - return MHD_YES; - } - - MHD_suspend_connection (connection); - /* See if the requested name is free */ - rd->iterating = - GNUNET_NAMESTORE_zone_iteration_start (namestore, - zone_key, - &iterate_error_cb, - rd, - &iterate_do_cb, - rd, - &iterate_done_cb, - rd); - return MHD_YES; - } - - return MHD_queue_response (connection, - MHD_HTTP_FORBIDDEN, - forbidden_page->response); - } - - return MHD_queue_response (connection, - MHD_HTTP_NOT_IMPLEMENTED, - forbidden_page->response); -} - - -/** - * Called when a request is completed. - * - * @param cls unused - * @param connection the connection - * @param ptr connection-specific data - * @param status status code - */ -static void -completed_cb (void *cls, - struct MHD_Connection *connection, - void **ptr, - enum MHD_RequestTerminationCode status) -{ - (void) cls; - (void) connection; - (void) status; - - struct RequestData *rd = *ptr; - - if (NULL == rd) - { - return; - } - - if (NULL == rd->body) - { - GNUNET_free (rd->body); - } - - if (NULL != rd->searching) - { - GNUNET_NAMESTORE_cancel (rd->searching); - } - - if (NULL != rd->register_name) - { - GNUNET_free (rd->register_name); - } - - if (NULL != rd->register_key) - { - GNUNET_free (rd->register_key); - } - - if (NULL != rd->iterating) - { - GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating); - } - - GNUNET_free (rd); -} - - -/** - * Called for each ego provided by the identity service. - * - * @param cls closure - * @param ego the ego - * @param ctx application-provided data for the ego - * @param name the ego name - */ -static void -identity_cb (void *cls, - struct GNUNET_IDENTITY_Ego *ego, - void **ctx, - const char *name) -{ - (void) cls; - (void) ctx; - - if ((NULL == name) || (0 != strcmp (name, zone))) - { - return; - } - - if (NULL == ego) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("No ego configured for `fcfsd` subsystem\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - - zone_key = GNUNET_IDENTITY_ego_get_private_key (ego); - - int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME; - do - { - httpd = MHD_start_daemon (flags, - (uint16_t) port, - NULL, NULL, - &create_response, NULL, - MHD_OPTION_CONNECTION_LIMIT, 128, - MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1, - MHD_OPTION_CONNECTION_TIMEOUT, 4 * 1024, - MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, - MHD_OPTION_END); - flags = MHD_USE_DEBUG; - } while (NULL == httpd && flags != MHD_USE_DEBUG); - - if (NULL == httpd) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Failed to start HTTP server\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - - run_httpd (); -} - - -/** - * Open a file on disk and generate a response object for it. - * - * @param name name of the file to open - * @param basedir directory where the file is located - * @return NULL on error - */ -static struct StaticPage * -open_static_page (const char *name, const char *basedir) -{ - char *fullname = NULL; - GNUNET_asprintf (&fullname, "%s/fcfsd-%s", basedir, name); - - struct GNUNET_DISK_FileHandle *f = - GNUNET_DISK_file_open (fullname, - GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_NONE); - GNUNET_free (fullname); - - if (NULL == f) - { - return NULL; - } - - off_t size = 0; - if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size)) - { - GNUNET_DISK_file_close (f); - return NULL; - } - - struct MHD_Response *response = - MHD_create_response_from_fd64 (size, - f->fd); - - if (NULL == response) - { - GNUNET_DISK_file_close (f); - return NULL; - } - - struct StaticPage *page = GNUNET_new (struct StaticPage); - page->handle = f; - page->size = (uint64_t) size; - page->response = response; - return page; -} - - -/** - * Called after the service is up. - * - * @param cls closure - * @param args remaining command line arguments - * @param cfgfile name of the configuration file - * @param cfg the service configuration - */ -static void -run_service (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - (void) cls; - (void) args; - (void) cfgfile; - - GNUNET_log_setup ("fcfsd", "WARNING", NULL); - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "fcfsd", - "RELATIVE_RECORD_EXPIRATION", - &record_exp)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("No expiration specified for records.\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, - "fcfsd", - "HTTPPORT", - &port)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("No port specified, using default value\n")); - } - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - - namestore = GNUNET_NAMESTORE_connect (cfg); - if (NULL == namestore) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Failed to connect to namestore\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - - identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL); - if (NULL == identity) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Failed to connect to identity\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - - char *basedir = NULL; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "fcfsd", - "HTMLDIR", - &basedir)) - { - basedir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); - } - - main_page = open_static_page ("index.html", basedir); - notfound_page = open_static_page ("notfound.html", basedir); - forbidden_page = open_static_page ("forbidden.html", basedir); - - GNUNET_free (basedir); - - if ((NULL == main_page) || (NULL == notfound_page) || (NULL == - forbidden_page) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to set up the daemon\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } -} - - -/** - * The main function of the fcfs daemon. - * - * @param argc number of arguments from the command line - * @param argv the command line arguments - * @return 0 successful exit, a different value otherwise - */ -int -main (int argc, char *const *argv) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_mandatory - (GNUNET_GETOPT_option_string ('z', - "zone", - "EGO", - gettext_noop ( - "name of the zone managed by FCFSD"), - &zone)), - GNUNET_GETOPT_OPTION_END - }; - - return ((GNUNET_OK == GNUNET_PROGRAM_run (argc, - argv, - "gnunet-namestore-fcfsd", - _ ( - "GNU Name System First-Come-First-Served name registration service"), - options, - &run_service, - NULL)) ? - 0 : - 1); -} diff --git a/src/namestore/gnunet-namestore-zonefile.c b/src/namestore/gnunet-namestore-zonefile.c deleted file mode 100644 index dfd438e94..000000000 --- a/src/namestore/gnunet-namestore-zonefile.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012, 2013, 2014, 2019, 2022 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file gnunet-namestore-dbtool.c - * @brief command line tool to manipulate the database backends for the namestore - * @author Martin Schanzenbach - * - */ -#include "platform.h" -#include -#include - -#define MAX_RECORDS_PER_NAME 50 - -/** - * Maximum length of a zonefile line - */ -#define MAX_ZONEFILE_LINE_LEN 4096 - -/** - * FIXME: Soft limit this? - */ -#define MAX_ZONEFILE_RECORD_DATA_LEN 2048 - -/** - * The record data under a single label. Reused. - * Hard limit. - */ -static struct GNUNET_GNSRECORD_Data rd[MAX_RECORDS_PER_NAME]; - -/** - * Current record $TTL to use - */ -static struct GNUNET_TIME_Relative ttl; - -/** - * Current origin - */ -static char origin[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; - -/** - * Number of records for currently parsed set - */ -static unsigned int rd_count = 0; - -/** - * Return code - */ -static int ret = 0; - -/** - * Name of the ego - */ -static char *ego_name = NULL; - -/** - * Currently read line or NULL on EOF - */ -static char *res; - -/** - * Statistics, how many published record sets - */ -static unsigned int published_sets = 0; - -/** - * Statistics, how many records published in aggregate - */ -static unsigned int published_records = 0; - - -/** - * Handle to identity lookup. - */ -static struct GNUNET_IDENTITY_EgoLookup *el; - -/** - * Private key for the our zone. - */ -static struct GNUNET_CRYPTO_PrivateKey zone_pkey; - -/** - * Queue entry for the 'add' operation. - */ -static struct GNUNET_NAMESTORE_QueueEntry *ns_qe; - -/** - * Handle to the namestore. - */ -static struct GNUNET_NAMESTORE_Handle *ns; - -/** - * Origin create operations - */ -static struct GNUNET_IDENTITY_Operation *id_op; - -/** - * Handle to IDENTITY - */ -static struct GNUNET_IDENTITY_Handle *id; - -/** - * Current configurataion - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Scheduled parse task - */ -static struct GNUNET_SCHEDULER_Task *parse_task; - -/** - * The current state of the parser - */ -static int state; - -enum ZonefileImportState -{ - - /* Uninitialized */ - ZS_READY, - - /* The initial state */ - ZS_ORIGIN_SET, - - /* The $ORIGIN has changed */ - ZS_ORIGIN_CHANGED, - - /* The record name/label has changed */ - ZS_NAME_CHANGED - -}; - - - -/** - * Task run on shutdown. Cleans up everything. - * - * @param cls unused - */ -static void -do_shutdown (void *cls) -{ - (void) cls; - if (NULL != ego_name) - GNUNET_free (ego_name); - if (NULL != el) - { - GNUNET_IDENTITY_ego_lookup_cancel (el); - el = NULL; - } - if (NULL != ns_qe) - GNUNET_NAMESTORE_cancel (ns_qe); - if (NULL != id_op) - GNUNET_IDENTITY_cancel (id_op); - if (NULL != ns) - GNUNET_NAMESTORE_disconnect (ns); - if (NULL != id) - GNUNET_IDENTITY_disconnect (id); - for (int i = 0; i < rd_count; i++) - { - void *rd_ptr = (void*) rd[i].data; - GNUNET_free (rd_ptr); - } - if (NULL != parse_task) - GNUNET_SCHEDULER_cancel (parse_task); -} - -static void -tx_end (void *cls, enum GNUNET_ErrorCode ec) -{ - ns_qe = NULL; - if (GNUNET_EC_NONE != ec) - { - fprintf (stderr, - _ ("Ego `%s' not known to identity service\n"), - ego_name); - GNUNET_SCHEDULER_shutdown (); - ret = -1; - } - GNUNET_SCHEDULER_shutdown (); -} - -static void -parse (void *cls); - -static char* -trim (char *line) -{ - char *ltrimmed = line; - int ltrimmed_len; - int quoted = 0; - - // Trim all whitespace to the left - while (*ltrimmed == ' ') - ltrimmed++; - ltrimmed_len = strlen (ltrimmed); - // Find the first occurence of an unqoted ';', which is our comment - for (int i = 0; i < ltrimmed_len; i++) - { - if (ltrimmed[i] == '"') - quoted = ! quoted; - if ((ltrimmed[i] != ';') || quoted) - continue; - ltrimmed[i] = '\0'; - } - ltrimmed_len = strlen (ltrimmed); - // Remove trailing whitespace - for (int i = ltrimmed_len; i > 0; i--) - { - if (ltrimmed[i - 1] != ' ') - break; - ltrimmed[i - 1] = '\0'; - } - ltrimmed_len = strlen (ltrimmed); - if (ltrimmed[ltrimmed_len - 1] == '\n') - ltrimmed[ltrimmed_len - 1] = ' '; - return ltrimmed; -} - -static char* -next_token (char *token) -{ - char *next = token; - while (*next == ' ') - next++; - return next; -} - -static int -parse_ttl (char *token, struct GNUNET_TIME_Relative *ttl) -{ - char *next; - unsigned int ttl_tmp; - - next = strchr (token, ';'); - if (NULL != next) - next[0] = '\0'; - next = strchr (token, ' '); - if (NULL != next) - next[0] = '\0'; - if (1 != sscanf (token, "%u", &ttl_tmp)) - { - fprintf (stderr, "Unable to parse TTL `%s'\n", token); - return GNUNET_SYSERR; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TTL is: %u\n", ttl_tmp); - ttl->rel_value_us = ttl_tmp * 1000 * 1000; - return GNUNET_OK; -} - -static int -parse_origin (char *token, char *origin) -{ - char *next; - next = strchr (token, ';'); - if (NULL != next) - next[0] = '\0'; - next = strchr (token, ' '); - if (NULL != next) - next[0] = '\0'; - strcpy (origin, token); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin is: %s\n", origin); - return GNUNET_OK; -} - -static void -origin_create_cb (void *cls, const struct GNUNET_CRYPTO_PrivateKey *pk, - enum GNUNET_ErrorCode ec) -{ - id_op = NULL; - if (GNUNET_EC_NONE != ec) - { - fprintf (stderr, "Error: %s\n", GNUNET_ErrorCode_get_hint (ec)); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - state = ZS_ORIGIN_SET; - zone_pkey = *pk; - parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); -} - -static void -origin_lookup_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) -{ - - el = NULL; - - if (NULL == ego) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "$ORIGIN %s does not exist, creating...\n", ego_name); - id_op = GNUNET_IDENTITY_create (id, ego_name, NULL, - GNUNET_PUBLIC_KEY_TYPE_ECDSA, // FIXME make configurable - origin_create_cb, - NULL); - return; - } - state = ZS_ORIGIN_SET; - zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); - parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); -} - -static void -add_continuation (void *cls, enum GNUNET_ErrorCode ec) -{ - ns_qe = NULL; - if (GNUNET_EC_NONE != ec) - { - fprintf (stderr, - _ ("Failed to store records...\n")); - GNUNET_SCHEDULER_shutdown (); - ret = -1; - } - if (ZS_ORIGIN_CHANGED == state) - { - if (NULL != ego_name) - GNUNET_free (ego_name); - ego_name = GNUNET_strdup (origin); - if (ego_name[strlen (ego_name) - 1] == '.') - ego_name[strlen (ego_name) - 1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Changing origin to %s\n", ego_name); - el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, - &origin_lookup_cb, NULL); - return; - } - parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); -} - - - -/** - * Main function that will be run. - * - * TODO: - * - We must assume that names are not repeated later in the zonefile because - * our _store APIs are replacing. No sure if that is common in zonefiles. - * - We must only actually store a record set when the name to store changes or - * the end of the file is reached. - * that way we can group them and add (see above). - * - We need to hope our string formats are compatible, but seems ok. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param cfg configuration - */ -static void -parse (void *cls) -{ - char buf[MAX_ZONEFILE_LINE_LEN]; - char payload[MAX_ZONEFILE_RECORD_DATA_LEN]; - char *next; - char *token; - char *payload_pos; - static char lastname[GNUNET_DNSPARSER_MAX_LABEL_LENGTH]; - char newname[GNUNET_DNSPARSER_MAX_LABEL_LENGTH]; - void *data; - size_t data_size; - int ttl_line = 0; - int type; - int bracket_unclosed = 0; - int quoted = 0; - - parse_task = NULL; - /* use filename provided as 1st argument (stdin by default) */ - int ln = 0; - while ((res = fgets (buf, sizeof(buf), stdin))) /* read each line of input */ - { - ln++; - ttl_line = 0; - token = trim (buf); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trimmed line (bracket %s): `%s'\n", - (bracket_unclosed > 0) ? "unclosed" : "closed", - token); - if ((0 == strlen (token)) || - ((1 == strlen (token)) && (' ' == *token))) - continue; // I guess we can safely ignore blank lines - if (bracket_unclosed == 0) - { - /* Payload is already parsed */ - payload_pos = payload; - /* Find space */ - next = strchr (token, ' '); - if (NULL == next) - { - fprintf (stderr, "Error at line %u: %s\n", ln, token); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - next[0] = '\0'; - next++; - if (0 == (strcmp (token, "$ORIGIN"))) - { - state = ZS_ORIGIN_CHANGED; - token = next_token (next); - } - else if (0 == (strcmp (token, "$TTL"))) - { - ttl_line = 1; - token = next_token (next); - } - else - { - if (0 == strcmp (token, "IN")) // Inherit name from before - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Old name: %s\n", lastname); - strcpy (newname, lastname); - token[strlen (token)] = ' '; - } - else if (token[strlen (token) - 1] != '.') // no fqdn - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: %s\n", token); - if (GNUNET_DNSPARSER_MAX_LABEL_LENGTH < strlen (token)) - { - fprintf (stderr, - _ ("Name `%s' is too long\n"), - token); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - strcpy (newname, token); - token = next_token (next); - } - else if (0 == strcmp (token, origin)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: @\n"); - strcpy (newname, "@"); - token = next_token (next); - } - else - { - if (strlen (token) < strlen (origin)) - { - fprintf (stderr, "Wrong origin: %s (expected %s)\n", token, origin); - break; // FIXME error? - } - if (0 != strcmp (token + (strlen (token) - strlen (origin)), origin)) - { - fprintf (stderr, "Wrong origin: %s (expected %s)\n", token, origin); - break; - } - token[strlen (token) - strlen (origin) - 1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New name: %s\n", token); - if (GNUNET_DNSPARSER_MAX_LABEL_LENGTH < strlen (token)) - { - fprintf (stderr, - _ ("Name `%s' is too long\n"), - token); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - strcpy (newname, token); - token = next_token (next); - } - if (0 != strcmp (newname, lastname) && - (0 < rd_count)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name changed %s->%s, storing record set of %u elements\n", - lastname, newname, - rd_count); - state = ZS_NAME_CHANGED; - } - else { - strcpy (lastname, newname); - } - } - - if (ttl_line) - { - if (GNUNET_SYSERR == parse_ttl (token, &ttl)) - { - fprintf (stderr, _ ("Failed to parse $TTL\n")); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - continue; - } - if (ZS_ORIGIN_CHANGED == state) - { - if (GNUNET_SYSERR == parse_origin (token, origin)) - { - fprintf (stderr, _ ("Failed to parse $ORIGIN from %s\n"), token); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - break; - } - if (ZS_READY == state) - { - fprintf (stderr, - _ ( - "You must provide $ORIGIN in your zonefile or via arguments (--zone)!\n")); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - // This is a record, let's go - if (MAX_RECORDS_PER_NAME == rd_count) - { - fprintf (stderr, - _ ("Only %u records per unique name supported.\n"), - MAX_RECORDS_PER_NAME); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - rd[rd_count].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - rd[rd_count].expiration_time = ttl.rel_value_us; - next = strchr (token, ' '); - if (NULL == next) - { - fprintf (stderr, "Error, last token: %s\n", token); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - break; - } - next[0] = '\0'; - next++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "class is: %s\n", token); - while (*next == ' ') - next++; - token = next; - next = strchr (token, ' '); - if (NULL == next) - { - fprintf (stderr, "Error\n"); - break; - } - next[0] = '\0'; - next++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "type is: %s\n", token); - type = GNUNET_GNSRECORD_typename_to_number (token); - rd[rd_count].record_type = type; - while (*next == ' ') - next++; - token = next; - } - for (int i = 0; i < strlen (token); i++) - { - if (token[i] == '"') - quoted = ! quoted; - if ((token[i] == '(') && ! quoted) - bracket_unclosed++; - if ((token[i] == ')') && ! quoted) - bracket_unclosed--; - } - memcpy (payload_pos, token, strlen (token)); - payload_pos += strlen (token); - if (bracket_unclosed > 0) - { - *payload_pos = ' '; - payload_pos++; - continue; - } - *payload_pos = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "data is: %s\n\n", payload); - if (GNUNET_OK != - GNUNET_GNSRECORD_string_to_value (type, payload, - &data, - &data_size)) - { - fprintf (stderr, - _ ("Data `%s' invalid\n"), - payload); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - rd[rd_count].data = data; - rd[rd_count].data_size = data_size; - if (ZS_NAME_CHANGED == state) - break; - rd_count++; - } - if (rd_count > 0) - { - ns_qe = GNUNET_NAMESTORE_records_store (ns, - &zone_pkey, - lastname, - rd_count, - rd, - &add_continuation, - NULL); - published_sets++; - published_records += rd_count; - for (int i = 0; i < rd_count; i++) - { - data = (void*) rd[i].data; - GNUNET_free (data); - } - if (ZS_NAME_CHANGED == state) - { - rd[0] = rd[rd_count]; // recover last rd parsed. - rd_count = 1; - strcpy (lastname, newname); - state = ZS_ORIGIN_SET; - } - else - rd_count = 0; - return; - } - if (ZS_ORIGIN_CHANGED == state) - { - if (NULL != ego_name) - GNUNET_free (ego_name); - ego_name = GNUNET_strdup (origin); - if (ego_name[strlen (ego_name) - 1] == '.') - ego_name[strlen (ego_name) - 1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Changing origin to %s\n", ego_name); - el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, - &origin_lookup_cb, NULL); - return; - } - printf ("Published %u records sets with total %u records\n", - published_sets, published_records); - ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, - &tx_end, - NULL); -} - -static void -tx_start (void *cls, enum GNUNET_ErrorCode ec) -{ - ns_qe = NULL; - if (GNUNET_EC_NONE != ec) - { - fprintf (stderr, - _ ("Ego `%s' not known to identity service\n"), - ego_name); - GNUNET_SCHEDULER_shutdown (); - ret = -1; - return; - } - parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); -} - -static void -identity_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego) -{ - - el = NULL; - if (NULL == ego) - { - if (NULL != ego_name) - { - fprintf (stderr, - _ ("Ego `%s' not known to identity service\n"), - ego_name); - - } - GNUNET_SCHEDULER_shutdown (); - ret = -1; - return; - } - zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); - sprintf (origin, "%s.", ego_name); - state = ZS_ORIGIN_SET; - ns_qe = GNUNET_NAMESTORE_transaction_begin (ns, - &tx_start, - NULL); -} - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *_cfg) -{ - cfg = _cfg; - ns = GNUNET_NAMESTORE_connect (cfg); - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg); - if (NULL == ns) - { - fprintf (stderr, - _ ("Failed to connect to NAMESTORE\n")); - return; - } - id = GNUNET_IDENTITY_connect (cfg, NULL, NULL); - if (NULL == id) - { - fprintf (stderr, - _ ("Failed to connect to IDENTITY\n")); - return; - } - if (NULL != ego_name) - el = GNUNET_IDENTITY_ego_lookup (cfg, ego_name, &identity_cb, (void *) cfg); - else - parse_task = GNUNET_SCHEDULER_add_now (&parse, NULL); - state = ZS_READY; -} - - -/** - * The main function for gnunet-namestore-dbtool. - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, char *const *argv) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_string ('z', - "zone", - "EGO", - gettext_noop ( - "name of the ego controlling the zone"), - &ego_name), - GNUNET_GETOPT_OPTION_END - }; - int lret; - - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - - GNUNET_log_setup ("gnunet-namestore-dbtool", "WARNING", NULL); - if (GNUNET_OK != - (lret = GNUNET_PROGRAM_run (argc, - argv, - "gnunet-namestore-zonefile", - _ ( - "GNUnet namestore database manipulation tool"), - options, - &run, - NULL))) - { - GNUNET_free_nz ((void *) argv); - return lret; - } - GNUNET_free_nz ((void *) argv); - return ret; -} diff --git a/src/namestore/gnunet-namestore.c b/src/namestore/gnunet-namestore.c deleted file mode 100644 index baa036ac7..000000000 --- a/src/namestore/gnunet-namestore.c +++ /dev/null @@ -1,2120 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012, 2013, 2014, 2019 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file gnunet-namestore.c - * @brief command line tool to manipulate the local zone - * @author Christian Grothoff - * - * TODO: - * - test - */ -#include "platform.h" -#include -#include -#include -#include -#include -#include - -/** - * The upper bound for the zone iteration interval - * (per record). - */ -#define WARN_RELATIVE_EXPIRATION_LIMIT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 15) - -/** - * Entry in record set for bulk processing. - */ -struct RecordSetEntry -{ - /** - * Kept in a linked list. - */ - struct RecordSetEntry *next; - - /** - * The record to add/remove. - */ - struct GNUNET_GNSRECORD_Data record; -}; - -/** - * The record marked for deletion - */ -struct MarkedRecord -{ - /** - * DLL - */ - struct MarkedRecord *next; - - /** - * DLL - */ - struct MarkedRecord *prev; - - /** - * Ego Identifier - */ - char *name; - - /** - * The zone key - */ - struct GNUNET_CRYPTO_PrivateKey key; -}; - -/** - * The default namestore ego - */ -struct EgoEntry -{ - /** - * DLL - */ - struct EgoEntry *next; - - /** - * DLL - */ - struct EgoEntry *prev; - - /** - * Ego Identifier - */ - char *identifier; - - /** - * The Ego - */ - struct GNUNET_IDENTITY_Ego *ego; -}; - -/** - * Handle to the namestore. - */ -static struct GNUNET_NAMESTORE_Handle *ns; - -/** - * Private key for the our zone. - */ -static struct GNUNET_CRYPTO_PrivateKey zone_pkey; - -/** - * Identity service handle - */ -static struct GNUNET_IDENTITY_Handle *idh; - -/** - * Name of the ego controlling the zone. - */ -static char *ego_name; - -/** - * Queue entry for the 'add-uri' operation. - */ -static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri; - -/** - * Queue entry for the 'add' operation. - */ -static struct GNUNET_NAMESTORE_QueueEntry *add_qe; - -/** - * Queue entry for the 'lookup' operation. - */ -static struct GNUNET_NAMESTORE_QueueEntry *get_qe; - -/** - * Queue entry for the 'reverse lookup' operation (in combination with a name). - */ -static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe; - -/** - * Marked record list - */ -static struct MarkedRecord *marked_head; - -/** - * Marked record list - */ -static struct MarkedRecord *marked_tail; - -/** - * Configuration handle - */ -const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Ego list - */ -static struct EgoEntry *ego_head; - -/** - * Ego list - */ -static struct EgoEntry *ego_tail; - -/** - * List iterator for the 'list' operation. - */ -static struct GNUNET_NAMESTORE_ZoneIterator *list_it; - -/** - * Run in read from stdin mode. - */ -static int read_from_stdin; - -/** - * Desired action is to list records. - */ -static int list; - -/** - * Desired action is to add a record. - */ -static int add; - -/** - * Desired action is to remove a record. - */ -static int del; - -/** - * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE) - */ -static int is_public; - -/** - * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW) - */ -static int is_shadow; - -/** - * Filter private records - */ -static int omit_private; - -/** - * Output in recordline format - */ -static int output_recordline; - - -/** - * Purge zone contents - */ -static int purge_zone; - -/** - * Do not filter maintenance records - */ -static int include_maintenance; - -/** - * Purge orphaned records - */ -static int purge_orphaned; - -/** - * List records and zone keys of orphaned records - */ -static int list_orphaned; - -/** - * Queue entry for the 'del' operation. - */ -static struct GNUNET_NAMESTORE_QueueEntry *del_qe; - -/** - * Queue entry for the 'set/replace' operation. - */ -static struct GNUNET_NAMESTORE_QueueEntry *set_qe; - -/** - * Queue entry for begin/commit - */ -static struct GNUNET_NAMESTORE_QueueEntry *ns_qe; - -/** - * Name of the records to add/list/remove. - */ -static char *name; - -/** - * Value of the record to add/remove. - */ -static char *value; - -/** - * URI to import. - */ -static char *uri; - -/** - * Reverse lookup to perform. - */ -static char *reverse_pkey; - -/** - * Type of the record to add/remove, NULL to remove all. - */ -static char *typestring; - -/** - * Desired expiration time. - */ -static char *expirationstring; - -/** - * Desired nick name. - */ -static char *nickstring; - -/** - * Global return value - */ -static int ret; - -/** - * Type string converted to DNS type value. - */ -static uint32_t type; - -/** - * Value in binary format. - */ -static void *data; - -/** - * Number of bytes in #data. - */ -static size_t data_size; - -/** - * Expiration string converted to numeric value. - */ -static uint64_t etime; - -/** - * Is expiration time relative or absolute time? - */ -static int etime_is_rel = GNUNET_SYSERR; - -/** - * Monitor handle. - */ -static struct GNUNET_NAMESTORE_ZoneMonitor *zm; - -/** - * Enables monitor mode. - */ -static int monitor; - -/** - * Entry in record set for processing records in bulk. - */ -static struct RecordSetEntry *recordset; - -/** - * Purge task - */ -static struct GNUNET_SCHEDULER_Task *purge_task; - -/** - * Parse expiration time. - * - * @param expirationstring text to parse - * @param[out] etime_is_rel set to #GNUNET_YES if time is relative - * @param[out] etime set to expiration time (abs or rel) - * @return #GNUNET_OK on success - */ -static int -parse_expiration (const char *expirationstring, - int *etime_is_rel, - uint64_t *etime) -{ - struct GNUNET_TIME_Relative etime_rel; - struct GNUNET_TIME_Absolute etime_abs; - - if (0 == strcmp (expirationstring, "never")) - { - *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; - *etime_is_rel = GNUNET_NO; - return GNUNET_OK; - } - if (GNUNET_OK == - GNUNET_STRINGS_fancy_time_to_relative (expirationstring, &etime_rel)) - { - *etime_is_rel = GNUNET_YES; - *etime = etime_rel.rel_value_us; - if (GNUNET_TIME_relative_cmp (etime_rel, <, WARN_RELATIVE_EXPIRATION_LIMIT)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Relative expiration times of less than %s are not recommended. To improve availability, consider increasing this value.\n", - GNUNET_STRINGS_relative_time_to_string ( - WARN_RELATIVE_EXPIRATION_LIMIT, GNUNET_NO)); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing record with relative expiration time of %s\n", - GNUNET_STRINGS_relative_time_to_string (etime_rel, GNUNET_NO)); - return GNUNET_OK; - } - if (GNUNET_OK == - GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, &etime_abs)) - { - *etime_is_rel = GNUNET_NO; - *etime = etime_abs.abs_value_us; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing record with absolute expiration time of %s\n", - GNUNET_STRINGS_absolute_time_to_string (etime_abs)); - return GNUNET_OK; - } - return GNUNET_SYSERR; -} - - -static int -parse_recordline (const char *line) -{ - struct RecordSetEntry **head = &recordset; - struct RecordSetEntry *r; - struct GNUNET_GNSRECORD_Data record; - char *cp; - char *tok; - char *saveptr; - void *raw_data; - - cp = GNUNET_strdup (line); - tok = strtok_r (cp, " ", &saveptr); - if (NULL == tok) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Missing entries in record line `%s'.\n"), - line); - GNUNET_free (cp); - return GNUNET_SYSERR; - } - record.record_type = GNUNET_GNSRECORD_typename_to_number (tok); - if (UINT32_MAX == record.record_type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ ("Unknown record type `%s'\n"), tok); - GNUNET_free (cp); - return GNUNET_SYSERR; - } - tok = strtok_r (NULL, " ", &saveptr); - if (NULL == tok) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Empty record line argument is not allowed.\n")); - GNUNET_free (cp); - return GNUNET_SYSERR; - } - if (1 != sscanf (tok, "%" SCNu64, &record.expiration_time)) - { - fprintf (stderr, - _ ("Error parsing expiration time %s.\n"), tok); - GNUNET_free (cp); - return GNUNET_SYSERR; - } - tok = strtok_r (NULL, " ", &saveptr); - if (NULL == tok) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Empty record line argument is not allowed.\n")); - GNUNET_free (cp); - return GNUNET_SYSERR; - } - record.flags = GNUNET_GNSRECORD_RF_NONE; - if (NULL != strchr (tok, (unsigned char) 'r')) - record.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - if (NULL == strchr (tok, (unsigned char) 'p')) /* p = public */ - record.flags |= GNUNET_GNSRECORD_RF_PRIVATE; - if (NULL != strchr (tok, (unsigned char) 'S')) - record.flags |= GNUNET_GNSRECORD_RF_SUPPLEMENTAL; - if (NULL != strchr (tok, (unsigned char) 's')) - record.flags |= GNUNET_GNSRECORD_RF_SHADOW; - if (NULL != strchr (tok, (unsigned char) 'C')) - record.flags |= GNUNET_GNSRECORD_RF_CRITICAL; - tok += strlen (tok) + 1; - if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value (record.record_type, - tok, - &raw_data, - &record.data_size)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Invalid record data for type %s: `%s'.\n"), - GNUNET_GNSRECORD_number_to_typename (record.record_type), - tok); - GNUNET_free (cp); - return GNUNET_SYSERR; - } - GNUNET_free (cp); - - r = GNUNET_malloc (sizeof(struct RecordSetEntry) + record.data_size); - r->next = *head; - record.data = &r[1]; - memcpy (&r[1], raw_data, record.data_size); - GNUNET_free (raw_data); - r->record = record; - *head = r; - return GNUNET_OK; -} - -static void -reset_handles (void) -{ - struct MarkedRecord *mrec; - struct MarkedRecord *mrec_tmp; - struct RecordSetEntry *rs_entry; - - rs_entry = recordset; - while (NULL != (rs_entry = recordset)) - { - recordset = recordset->next; - GNUNET_free (rs_entry); - } - recordset = NULL; - if (NULL != ego_name) - { - GNUNET_free (ego_name); - ego_name = NULL; - } - if (NULL != name) - { - GNUNET_free (name); - name = NULL; - } - if (NULL != value) - { - GNUNET_free (value); - value = NULL; - } - if (NULL != uri) - { - GNUNET_free (uri); - uri = NULL; - } - if (NULL != expirationstring) - { - GNUNET_free (expirationstring); - expirationstring = NULL; - } - if (NULL != purge_task) - { - GNUNET_SCHEDULER_cancel (purge_task); - purge_task = NULL; - } - for (mrec = marked_head; NULL != mrec;) - { - mrec_tmp = mrec; - mrec = mrec->next; - GNUNET_free (mrec_tmp->name); - GNUNET_free (mrec_tmp); - } - if (NULL != list_it) - { - GNUNET_NAMESTORE_zone_iteration_stop (list_it); - list_it = NULL; - } - if (NULL != add_qe) - { - GNUNET_NAMESTORE_cancel (add_qe); - add_qe = NULL; - } - if (NULL != set_qe) - { - GNUNET_NAMESTORE_cancel (set_qe); - set_qe = NULL; - } - if (NULL != add_qe_uri) - { - GNUNET_NAMESTORE_cancel (add_qe_uri); - add_qe_uri = NULL; - } - if (NULL != get_qe) - { - GNUNET_NAMESTORE_cancel (get_qe); - get_qe = NULL; - } - if (NULL != del_qe) - { - GNUNET_NAMESTORE_cancel (del_qe); - del_qe = NULL; - } - if (NULL != reverse_qe) - { - GNUNET_NAMESTORE_cancel (reverse_qe); - reverse_qe = NULL; - } - memset (&zone_pkey, 0, sizeof(zone_pkey)); - if (NULL != zm) - { - GNUNET_NAMESTORE_zone_monitor_stop (zm); - zm = NULL; - } - if (NULL != data) - { - GNUNET_free (data); - data = NULL; - } - if (NULL != typestring) - { - GNUNET_free (typestring); - typestring = NULL; - } - list = 0; - is_public = 0; - is_shadow = 0; - purge_zone = 0; -} - - - -/** - * Task run on shutdown. Cleans up everything. - * - * @param cls unused - */ -static void -do_shutdown (void *cls) -{ - struct EgoEntry *ego_entry; - struct EgoEntry *ego_tmp; - (void) cls; - - reset_handles (); - if (NULL != ns_qe) - { - GNUNET_NAMESTORE_cancel (ns_qe); - ns_qe = NULL; - } - if (NULL != ns) - { - GNUNET_NAMESTORE_disconnect (ns); - ns = NULL; - } - if (NULL != idh) - { - GNUNET_IDENTITY_disconnect (idh); - idh = NULL; - } - for (ego_entry = ego_head; NULL != ego_entry;) - { - ego_tmp = ego_entry; - ego_entry = ego_entry->next; - GNUNET_free (ego_tmp->identifier); - GNUNET_free (ego_tmp); - } -} - -static void -commit_cb (void *cls, enum GNUNET_ErrorCode ec) -{ - ns_qe = NULL; - if (GNUNET_EC_NONE != ec) - { - fprintf (stderr, "Failed to commit to namestore: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - ret = 1; - } - GNUNET_SCHEDULER_shutdown (); -} - -static void -process_command_stdin (); - - -static void -finish_command (void) -{ - reset_handles (); - if (read_from_stdin) - { - process_command_stdin (); - return; - } - ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, &commit_cb, NULL); -} - - -static void -add_continuation (void *cls, enum GNUNET_ErrorCode ec) -{ - struct GNUNET_NAMESTORE_QueueEntry **qe = cls; - - *qe = NULL; - if (GNUNET_EC_NONE != ec) - { - fprintf (stderr, - _ ("Adding record failed: %s\n"), - GNUNET_ErrorCode_get_hint (ec)); - if (GNUNET_EC_NAMESTORE_RECORD_EXISTS != ec) - ret = 1; - } - ret = 0; - finish_command (); -} - - -static void -del_continuation (void *cls, enum GNUNET_ErrorCode ec) -{ - (void) cls; - del_qe = NULL; - if (GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND == ec) - { - fprintf (stderr, - _ ("Deleting record failed: %s\n"), GNUNET_ErrorCode_get_hint ( - ec)); - } - finish_command (); -} - -static void -purge_next_record (void *cls); - -static void -marked_deleted (void *cls, enum GNUNET_ErrorCode ec) -{ - del_qe = NULL; - if (GNUNET_EC_NONE != ec) - { - fprintf (stderr, - _ ("Deleting record failed: %s\n"), - GNUNET_ErrorCode_get_hint (ec)); - } - purge_task = GNUNET_SCHEDULER_add_now (&purge_next_record, NULL); -} - - -static void -purge_next_record (void *cls) -{ - struct MarkedRecord *mrec; - purge_task = NULL; - - if (NULL == marked_head) - { - ret = 0; - finish_command (); - return; - } - mrec = marked_head; - GNUNET_CONTAINER_DLL_remove (marked_head, - marked_tail, - mrec); - del_qe = GNUNET_NAMESTORE_records_store (ns, - &mrec->key, - mrec->name, - 0, NULL, - &marked_deleted, - NULL); - GNUNET_free (mrec->name); - GNUNET_free (mrec); -} - -/** - * Function called when we are done with a zone iteration. - */ -static void -zone_iteration_finished (void *cls) -{ - (void) cls; - list_it = NULL; - if (purge_orphaned || purge_zone) - { - purge_task = GNUNET_SCHEDULER_add_now (&purge_next_record, NULL); - return; - } - ret = 0; - finish_command (); -} - - -/** - * Function called when we encountered an error in a zone iteration. - */ -static void -zone_iteration_error_cb (void *cls) -{ - (void) cls; - list_it = NULL; - fprintf (stderr, "Error iterating over zone\n"); - ret = 1; - finish_command (); -} - -static void -collect_zone_records_to_purge (const struct - GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct MarkedRecord *mrec; - - mrec = GNUNET_new (struct MarkedRecord); - mrec->key = *zone_key; - mrec->name = GNUNET_strdup (rname); - GNUNET_CONTAINER_DLL_insert (marked_head, - marked_tail, - mrec); -} - - -static void -collect_orphans (const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct EgoEntry *ego; - struct MarkedRecord *orphan; - int is_orphaned = 1; - - for (ego = ego_head; NULL != ego; ego = ego->next) - { - if (0 == memcmp (GNUNET_IDENTITY_ego_get_private_key (ego->ego), - zone_key, - sizeof (*zone_key))) - { - is_orphaned = 0; - break; - } - } - if (is_orphaned) - { - orphan = GNUNET_new (struct MarkedRecord); - orphan->key = *zone_key; - orphan->name = GNUNET_strdup (rname); - GNUNET_CONTAINER_DLL_insert (marked_head, - marked_tail, - orphan); - } -} - -/** - * Process a record that was stored in the namestore. - * - * @param rname name that is being mapped (at most 255 characters long) - * @param rd_len number of entries in @a rd array - * @param rd array of records with data to store - */ -static void -display_record (const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd) -{ - const char *typestr; - char *s; - const char *ets; - struct GNUNET_TIME_Absolute at; - struct GNUNET_TIME_Relative rt; - struct EgoEntry *ego; - int have_record; - int is_orphaned = 1; - char *orphaned_str; - - if ((NULL != name) && (0 != strcmp (name, rname))) - return; - have_record = GNUNET_NO; - for (unsigned int i = 0; i < rd_len; i++) - { - if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) && - (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT))) - continue; - if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type)) - continue; - have_record = GNUNET_YES; - break; - } - if (GNUNET_NO == have_record) - return; - for (ego = ego_head; NULL != ego; ego = ego->next) - { - if (0 == memcmp (GNUNET_IDENTITY_ego_get_private_key (ego->ego), - zone_key, - sizeof (*zone_key))) - { - is_orphaned = 0; - break; - } - } - if (list_orphaned && ! is_orphaned) - return; - if (! list_orphaned && is_orphaned) - return; - orphaned_str = GNUNET_CRYPTO_private_key_to_string (zone_key); - fprintf (stdout, "%s.%s:\n", rname, is_orphaned ? orphaned_str : - ego->identifier); - GNUNET_free (orphaned_str); - if (NULL != typestring) - type = GNUNET_GNSRECORD_typename_to_number (typestring); - else - type = GNUNET_GNSRECORD_TYPE_ANY; - for (unsigned int i = 0; i < rd_len; i++) - { - if ((GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) && - (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT))) - continue; - if ((type != rd[i].record_type) && (GNUNET_GNSRECORD_TYPE_ANY != type)) - continue; - typestr = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type); - s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type, - rd[i].data, - rd[i].data_size); - if (NULL == s) - { - fprintf (stdout, - _ ("\tCorrupt or unsupported record of type %u\n"), - (unsigned int) rd[i].record_type); - continue; - } - if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) - { - rt.rel_value_us = rd[i].expiration_time; - ets = GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_YES); - } - else - { - at.abs_value_us = rd[i].expiration_time; - ets = GNUNET_STRINGS_absolute_time_to_string (at); - } - char flgstr[16]; - sprintf (flgstr, "[%s%s%s%s%s]", - (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE) ? "" : "p", - (rd[i].flags & GNUNET_GNSRECORD_RF_SUPPLEMENTAL) ? "S" : "", - (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION) ? "r" : "", - (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW) ? "S" : "", - (rd[i].flags & GNUNET_GNSRECORD_RF_CRITICAL) ? "C" : ""); - if (output_recordline) - fprintf (stdout, - " %s %" PRIu64 " %s %s\n", - typestr, - rd[i].expiration_time, - flgstr, - s); - else - fprintf (stdout, - "\t%s: %s (%s)\t%s\t%s\n", - typestr, - s, - ets, - (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE)) ? "PRIVATE" - : "PUBLIC", - (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW)) ? "SHADOW" - : ""); - GNUNET_free (s); - } - // fprintf (stdout, "%s", "\n"); -} - -static void -purge_zone_iterator (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd, - struct GNUNET_TIME_Absolute expiry) -{ - (void) cls; - (void) zone_key; - (void) expiry; - collect_zone_records_to_purge (zone_key, rname, rd_len, rd); - GNUNET_NAMESTORE_zone_iterator_next (list_it, 1); -} - - -static void -purge_orphans_iterator (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd, - struct GNUNET_TIME_Absolute expiry) -{ - (void) cls; - (void) zone_key; - (void) expiry; - collect_orphans (zone_key, rname, rd_len, rd); - GNUNET_NAMESTORE_zone_iterator_next (list_it, 1); -} - - -/** - * Process a record that was stored in the namestore. - * - * @param cls closure - * @param zone_key private key of the zone - * @param rname name that is being mapped (at most 255 characters long) - * @param rd_len number of entries in @a rd array - * @param rd array of records with data to store - */ -static void -display_record_iterator (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd, - struct GNUNET_TIME_Absolute expiry) -{ - (void) cls; - (void) zone_key; - (void) expiry; - display_record (zone_key, rname, rd_len, rd); - GNUNET_NAMESTORE_zone_iterator_next (list_it, 1); -} - - -/** - * Process a record that was stored in the namestore. - * - * @param cls closure - * @param zone_key private key of the zone - * @param rname name that is being mapped (at most 255 characters long) - * @param rd_len number of entries in @a rd array - * @param rd array of records with data to store - */ -static void -display_record_monitor (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd, - struct GNUNET_TIME_Absolute expiry) -{ - (void) cls; - (void) zone_key; - (void) expiry; - display_record (zone_key, rname, rd_len, rd); - GNUNET_NAMESTORE_zone_monitor_next (zm, 1); -} - - -/** - * Process a record that was stored in the namestore. - * - * @param cls closure - * @param zone_key private key of the zone - * @param rname name that is being mapped (at most 255 characters long) - * @param rd_len number of entries in @a rd array - * @param rd array of records with data to store - */ -static void -display_record_lookup (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd) -{ - (void) cls; - (void) zone_key; - get_qe = NULL; - display_record (zone_key, rname, rd_len, rd); - finish_command (); -} - - -/** - * Function called once we are in sync in monitor mode. - * - * @param cls NULL - */ -static void -sync_cb (void *cls) -{ - (void) cls; - fprintf (stdout, "%s", "Monitor is now in sync.\n"); -} - - -/** - * Function called on errors while monitoring. - * - * @param cls NULL - */ -static void -monitor_error_cb (void *cls) -{ - (void) cls; - fprintf (stderr, "%s", "Monitor disconnected and out of sync.\n"); -} - - -/** - * Function called on errors while monitoring. - * - * @param cls NULL - */ -static void -lookup_error_cb (void *cls) -{ - (void) cls; - get_qe = NULL; - fprintf (stderr, "%s", "Failed to lookup record.\n"); - finish_command (); -} - - -/** - * Function called if lookup fails. - */ -static void -add_error_cb (void *cls) -{ - (void) cls; - add_qe = NULL; - GNUNET_break (0); - ret = 1; - finish_command (); -} - - -/** - * We're storing a record; this function is given the existing record - * so that we can merge the information. - * - * @param cls closure, unused - * @param zone_key private key of the zone - * @param rec_name name that is being mapped (at most 255 characters long) - * @param rd_count number of entries in @a rd array - * @param rd array of records with data to store - */ -static void -get_existing_record (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rec_name, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct GNUNET_GNSRECORD_Data rdn[rd_count + 1]; - struct GNUNET_GNSRECORD_Data *rde; - - (void) cls; - (void) zone_key; - add_qe = NULL; - if (0 != strcmp (rec_name, name)) - { - GNUNET_break (0); - ret = 1; - finish_command (); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received %u records for name `%s'\n", - rd_count, - rec_name); - for (unsigned int i = 0; i < rd_count; i++) - { - switch (rd[i].record_type) - { - case GNUNET_DNSPARSER_TYPE_SOA: - if (GNUNET_DNSPARSER_TYPE_SOA == type) - { - fprintf ( - stderr, - _ ( - "A SOA record exists already under `%s', cannot add a second SOA to the same zone.\n"), - rec_name); - ret = 1; - finish_command (); - return; - } - break; - } - } - memset (rdn, 0, sizeof(struct GNUNET_GNSRECORD_Data)); - GNUNET_memcpy (&rdn[1], rd, rd_count * sizeof(struct GNUNET_GNSRECORD_Data)); - rde = &rdn[0]; - rde->data = data; - rde->data_size = data_size; - rde->record_type = type; - if (1 == is_shadow) - rde->flags |= GNUNET_GNSRECORD_RF_SHADOW; - if (1 != is_public) - rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE; - rde->expiration_time = etime; - if (GNUNET_YES == etime_is_rel) - rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - else if (GNUNET_NO != etime_is_rel) - rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; - GNUNET_assert (NULL != name); - add_qe = GNUNET_NAMESTORE_records_store (ns, - &zone_pkey, - name, - rd_count + 1, - rde, - &add_continuation, - &add_qe); -} - - -/** - * Function called if we encountered an error in zone-to-name. - */ -static void -reverse_error_cb (void *cls) -{ - (void) cls; - reverse_qe = NULL; - fprintf (stdout, "%s.zkey\n", reverse_pkey); -} - - -/** - * Function called with the result of our attempt to obtain a name for a given - * public key. - * - * @param cls NULL - * @param zone private key of the zone; NULL on disconnect - * @param label label of the records; NULL on disconnect - * @param rd_count number of entries in @a rd array, 0 if label was deleted - * @param rd array of records with data to store - */ -static void -handle_reverse_lookup (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - (void) cls; - (void) zone; - (void) rd_count; - (void) rd; - reverse_qe = NULL; - if (NULL == label) - fprintf (stdout, "%s\n", reverse_pkey); - else - fprintf (stdout, "%s.%s\n", label, ego_name); - finish_command (); -} - - -/** - * Function called if lookup for deletion fails. - */ -static void -del_lookup_error_cb (void *cls) -{ - (void) cls; - del_qe = NULL; - GNUNET_break (0); - ret = 1; - finish_command (); -} - - -/** - * We were asked to delete something; this function is called with - * the existing records. Now we should determine what should be - * deleted and then issue the deletion operation. - * - * @param cls NULL - * @param zone private key of the zone we are deleting from - * @param label name of the records we are editing - * @param rd_count size of the @a rd array - * @param rd existing records - */ -static void -del_monitor (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct GNUNET_GNSRECORD_Data rdx[rd_count]; - unsigned int rd_left; - uint32_t type; - char *vs; - - (void) cls; - (void) zone; - del_qe = NULL; - if (0 == rd_count) - { - fprintf (stderr, - _ ( - "There are no records under label `%s' that could be deleted.\n"), - label); - ret = 1; - finish_command (); - return; - } - if ((NULL == value) && (NULL == typestring)) - { - /* delete everything */ - del_qe = GNUNET_NAMESTORE_records_store (ns, - &zone_pkey, - name, - 0, - NULL, - &del_continuation, - NULL); - return; - } - rd_left = 0; - if (NULL != typestring) - type = GNUNET_GNSRECORD_typename_to_number (typestring); - else - type = GNUNET_GNSRECORD_TYPE_ANY; - for (unsigned int i = 0; i < rd_count; i++) - { - vs = NULL; - if (! (((GNUNET_GNSRECORD_TYPE_ANY == type) || - (rd[i].record_type == type)) && - ((NULL == value) || - (NULL == - (vs = (GNUNET_GNSRECORD_value_to_string (rd[i].record_type, - rd[i].data, - rd[i].data_size)))) || - (0 == strcmp (vs, value))))) - rdx[rd_left++] = rd[i]; - GNUNET_free (vs); - } - if (rd_count == rd_left) - { - /* nothing got deleted */ - fprintf ( - stderr, - _ ( - "There are no records under label `%s' that match the request for deletion.\n"), - label); - finish_command (); - return; - } - /* delete everything but what we copied to 'rdx' */ - del_qe = GNUNET_NAMESTORE_records_store (ns, - &zone_pkey, - name, - rd_left, - rdx, - &del_continuation, - NULL); -} - - -static void -replace_cont (void *cls, enum GNUNET_ErrorCode ec) -{ - (void) cls; - - set_qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, - _ ("%s\n"), - GNUNET_ErrorCode_get_hint (ec)); - ret = 1; /* fail from 'main' */ - } - finish_command (); -} - - -/** - * We have obtained the zone's private key, so now process - * the main commands using it. - * - * @param cfg configuration to use - */ -static void -run_with_zone_pkey (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_GNSRECORD_Data rd; - enum GNUNET_GNSRECORD_Filter filter_flags = GNUNET_GNSRECORD_FILTER_NONE; - - if (omit_private) - filter_flags |= GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE; - if (include_maintenance) - filter_flags |= GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE; - if (! (add | del | list | (NULL != nickstring) | (NULL != uri) - | (NULL != reverse_pkey) | (NULL != recordset) | (monitor) - | (purge_orphaned) | (list_orphaned) | (purge_zone)) ) - { - /* nothing more to be done */ - fprintf (stderr, _ ("No options given\n")); - finish_command (); - return; - } - - if (NULL != recordset) - { - /* replace entire record set */ - unsigned int rd_count; - struct GNUNET_GNSRECORD_Data *rd; - - /* FIXME: We could easily support append and delete with this as well */ - if (! add) - { - fprintf (stderr, _ ("Recordlines only work with option `%s'\n"), - "-a"); - ret = 1; - finish_command (); - return; - } - if (NULL == name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-n", - _ ("name")); - ret = 1; - finish_command (); - return; - } - rd_count = 0; - for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next) - rd_count++; - rd = GNUNET_new_array (rd_count, struct GNUNET_GNSRECORD_Data); - rd_count = 0; - for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next) - { - rd[rd_count] = e->record; - rd_count++; - } - set_qe = GNUNET_NAMESTORE_records_store (ns, - &zone_pkey, - name, - rd_count, - rd, - &replace_cont, - NULL); - GNUNET_free (rd); - return; - } - if (NULL != nickstring) - { - if (0 == strlen (nickstring)) - { - fprintf (stderr, _ ("Invalid nick `%s'\n"), nickstring); - ret = 1; - finish_command (); - return; - } - add = 1; - typestring = GNUNET_strdup (GNUNET_GNSRECORD_number_to_typename ( - GNUNET_GNSRECORD_TYPE_NICK)); - name = GNUNET_strdup (GNUNET_GNS_EMPTY_LABEL_AT); - value = GNUNET_strdup (nickstring); - is_public = 0; - expirationstring = GNUNET_strdup ("never"); - GNUNET_free (nickstring); - nickstring = NULL; - } - - if (add) - { - if (NULL == ego_name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-z", - _ ("add")); - ret = 1; - finish_command (); - return; - } - if (NULL == name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-n", - _ ("add")); - ret = 1; - finish_command (); - return; - } - if (NULL == typestring) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-t", - _ ("add")); - ret = 1; - finish_command (); - return; - } - type = GNUNET_GNSRECORD_typename_to_number (typestring); - if (UINT32_MAX == type) - { - fprintf (stderr, _ ("Unsupported type `%s'\n"), typestring); - ret = 1; - finish_command (); - return; - } - if ((GNUNET_DNSPARSER_TYPE_SRV == type) || - (GNUNET_DNSPARSER_TYPE_TLSA == type) || - (GNUNET_DNSPARSER_TYPE_OPENPGPKEY == type)) - { - fprintf (stderr, - _ ("For DNS record types `SRV', `TLSA' and `OPENPGPKEY'")); - fprintf (stderr, ", please use a `BOX' record instead\n"); - ret = 1; - finish_command (); - return; - } - if (NULL == value) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-V", - _ ("add")); - ret = 1; - finish_command (); - return; - } - if (GNUNET_OK != - GNUNET_GNSRECORD_string_to_value (type, value, &data, &data_size)) - { - fprintf (stderr, - _ ("Value `%s' invalid for record type `%s'\n"), - value, - typestring); - ret = 1; - finish_command (); - return; - } - if (NULL == expirationstring) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-e", - _ ("add")); - ret = 1; - finish_command (); - return; - } - if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime)) - { - fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring); - ret = 1; - finish_command (); - return; - } - add_qe = GNUNET_NAMESTORE_records_lookup (ns, - &zone_pkey, - name, - &add_error_cb, - NULL, - &get_existing_record, - NULL); - } - if (del) - { - if (NULL == ego_name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-z", - _ ("del")); - ret = 1; - finish_command (); - return; - } - if (NULL == name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-n", - _ ("del")); - ret = 1; - finish_command (); - return; - } - del_qe = GNUNET_NAMESTORE_records_lookup (ns, - &zone_pkey, - name, - &del_lookup_error_cb, - NULL, - &del_monitor, - NULL); - } - if (purge_orphaned) - { - list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns, - NULL, - &zone_iteration_error_cb, - NULL, - &purge_orphans_iterator, - NULL, - &zone_iteration_finished, - NULL, - filter_flags); - - } - else if (purge_zone) - { - if (NULL == ego_name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-z", - _ ("purge-zone")); - ret = 1; - finish_command (); - return; - } - list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns, - &zone_pkey, - &zone_iteration_error_cb, - NULL, - &purge_zone_iterator, - NULL, - &zone_iteration_finished, - NULL, - filter_flags); - - } - else if (list || list_orphaned) - { - if (NULL != name) - { - if (NULL == ego_name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-z", - _ ("list")); - ret = 1; - finish_command (); - return; - } - get_qe = GNUNET_NAMESTORE_records_lookup (ns, - &zone_pkey, - name, - &lookup_error_cb, - NULL, - &display_record_lookup, - NULL); - } - else - list_it = GNUNET_NAMESTORE_zone_iteration_start2 (ns, - (NULL == ego_name) ? - NULL : &zone_pkey, - &zone_iteration_error_cb, - NULL, - &display_record_iterator, - NULL, - &zone_iteration_finished, - NULL, - filter_flags); - } - if (NULL != reverse_pkey) - { - struct GNUNET_CRYPTO_PublicKey pubkey; - - if (NULL == ego_name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-z", - _ ("reverse-pkey")); - ret = 1; - finish_command (); - return; - } - if (GNUNET_OK != - GNUNET_CRYPTO_public_key_from_string (reverse_pkey, - &pubkey)) - { - fprintf (stderr, - _ ("Invalid public key for reverse lookup `%s'\n"), - reverse_pkey); - ret = 1; - finish_command (); - return; - } - reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns, - &zone_pkey, - &pubkey, - &reverse_error_cb, - NULL, - &handle_reverse_lookup, - NULL); - } - if (NULL != uri) - { - char sh[105]; - char sname[64]; - struct GNUNET_CRYPTO_PublicKey pkey; - if (NULL == ego_name) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-z", - _ ("uri")); - ret = 1; - finish_command (); - return; - } - - memset (sh, 0, 105); - memset (sname, 0, 64); - - if ((2 != (sscanf (uri, "gnunet://gns/%58s/%63s", sh, sname))) || - (GNUNET_OK != - GNUNET_CRYPTO_public_key_from_string (sh, &pkey))) - { - fprintf (stderr, _ ("Invalid URI `%s'\n"), uri); - ret = 1; - finish_command (); - return; - } - if (NULL == expirationstring) - { - fprintf (stderr, - _ ("Missing option `%s' for operation `%s'\n"), - "-e", - _ ("add")); - ret = 1; - finish_command (); - return; - } - if (GNUNET_OK != parse_expiration (expirationstring, &etime_is_rel, &etime)) - { - fprintf (stderr, _ ("Invalid time format `%s'\n"), expirationstring); - ret = 1; - finish_command (); - return; - } - memset (&rd, 0, sizeof(rd)); - rd.data = &pkey; - rd.data_size = GNUNET_CRYPTO_public_key_get_length (&pkey); - rd.record_type = ntohl (pkey.type); - rd.expiration_time = etime; - if (GNUNET_YES == etime_is_rel) - rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - if (1 == is_shadow) - rd.flags |= GNUNET_GNSRECORD_RF_SHADOW; - add_qe_uri = GNUNET_NAMESTORE_records_store (ns, - &zone_pkey, - sname, - 1, - &rd, - &add_continuation, - &add_qe_uri); - } - if (monitor) - { - zm = GNUNET_NAMESTORE_zone_monitor_start2 (cfg, - (NULL != ego_name) ? - &zone_pkey : NULL, - GNUNET_YES, - &monitor_error_cb, - NULL, - &display_record_monitor, - NULL, - &sync_cb, - NULL, - filter_flags); - } -} - -#define MAX_LINE_LEN 4086 - -#define MAX_ARGS 20 - -static int -get_identity_for_string (const char *str, - struct GNUNET_CRYPTO_PrivateKey *zk) -{ - const struct GNUNET_CRYPTO_PrivateKey *privkey; - struct GNUNET_CRYPTO_PublicKey pubkey; - struct GNUNET_CRYPTO_PublicKey ego_pubkey; - struct EgoEntry *ego_entry; - - if (GNUNET_OK == GNUNET_CRYPTO_public_key_from_string (str, - &pubkey)) - { - for (ego_entry = ego_head; - NULL != ego_entry; ego_entry = ego_entry->next) - { - privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &ego_pubkey); - if (0 == memcmp (&ego_pubkey, &pubkey, sizeof (pubkey))) - { - *zk = *privkey; - return GNUNET_OK; - } - } - } - else - { - for (ego_entry = ego_head; NULL != ego_entry; ego_entry = ego_entry->next) - { - /** FIXME: Check for zTLD? **/ - if (0 != strcmp (str, ego_entry->identifier)) - continue; - *zk = *GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - return GNUNET_OK; - } - } - return GNUNET_NO; -} - -static void -process_command_stdin () -{ - char buf[MAX_LINE_LEN]; - static struct GNUNET_CRYPTO_PrivateKey next_zone_key; - static char next_name[GNUNET_DNSPARSER_MAX_NAME_LENGTH]; - static int finished = GNUNET_NO; - static int have_next_zonekey = GNUNET_NO; - int zonekey_set = GNUNET_NO; - char *tmp; - - - if (GNUNET_YES == have_next_zonekey) - { - zone_pkey = next_zone_key; - if (NULL != name) - GNUNET_free (name); - name = GNUNET_strdup (next_name); - zonekey_set = GNUNET_YES; - } - while (NULL != fgets (buf, sizeof (buf), stdin)) - { - if (1 >= strlen (buf)) - continue; - if (buf[strlen (buf) - 1] == '\n') - buf[strlen (buf) - 1] = '\0'; - /** - * Check if this is a new name. If yes, and we have records, store them. - */ - if (buf[strlen (buf) - 1] == ':') - { - memset (next_name, 0, sizeof (next_name)); - strncpy (next_name, buf, strlen (buf) - 1); - tmp = strchr (next_name, '.'); - if (NULL == tmp) - { - fprintf (stderr, "Error parsing name `%s'\n", next_name); - ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, &commit_cb, NULL); - ret = 1; - return; - } - if (GNUNET_OK != get_identity_for_string (tmp + 1, &next_zone_key)) - { - fprintf (stderr, "Error parsing zone name `%s'\n", tmp + 1); - ret = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - *tmp = '\0'; - have_next_zonekey = GNUNET_YES; - /* Run a command for the previous record set */ - if (NULL != recordset) - { - run_with_zone_pkey (cfg); - return; - } - zone_pkey = next_zone_key; - if (NULL != name) - GNUNET_free (name); - name = GNUNET_strdup (next_name); - zonekey_set = GNUNET_YES; - continue; - } - if (GNUNET_NO == zonekey_set) - { - fprintf (stderr, "Warning, encountered recordline without zone\n"); - continue; - } - parse_recordline (buf); - } - if (GNUNET_NO == finished) - { - if (NULL != recordset) - { - if (GNUNET_YES == zonekey_set) - { - run_with_zone_pkey (cfg); /** one last time **/ - finished = GNUNET_YES; - return; - } - fprintf (stderr, "Warning, encountered recordline without zone\n"); - } - } - ns_qe = GNUNET_NAMESTORE_transaction_commit (ns, &commit_cb, NULL); - return; -} - - -static void -begin_cb (void *cls, enum GNUNET_ErrorCode ec) -{ - ns_qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to start transaction: %s\n", - GNUNET_ErrorCode_get_hint (ec)); - GNUNET_SCHEDULER_shutdown (); - return; - } - if (read_from_stdin) - { - process_command_stdin (); - return; - } - run_with_zone_pkey (cfg); -} - - -/** - * Function called with ALL of the egos known to the - * identity service, used on startup if the user did - * not specify a zone on the command-line. - * Once the iteration is done (@a ego is NULL), we - * ask for the default ego for "namestore". - * - * @param cls a `struct GNUNET_CONFIGURATION_Handle` - * @param ego an ego, NULL for end of iteration - * @param ctx NULL - * @param name name associated with @a ego - */ -static void -id_connect_cb (void *cls, - struct GNUNET_IDENTITY_Ego *ego, - void **ctx, - const char *name) -{ - struct GNUNET_CRYPTO_PublicKey pk; - struct EgoEntry *ego_entry; - - (void) ctx; - (void) name; - if ((NULL != name) && (NULL != ego)) - { - ego_entry = GNUNET_new (struct EgoEntry); - GNUNET_IDENTITY_ego_get_public_key (ego, &pk); - ego_entry->ego = ego; - ego_entry->identifier = GNUNET_strdup (name); - GNUNET_CONTAINER_DLL_insert_tail (ego_head, - ego_tail, - ego_entry); - if ((NULL != ego_name) && - (0 == strcmp (name, ego_name))) - zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); - return; - } - if (NULL != ego) - return; - ns_qe = GNUNET_NAMESTORE_transaction_begin (ns, &begin_cb, (void *) cfg); -} - - - - - -/** - * Main function that will be run. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param cfg configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *_cfg) -{ - (void) cls; - (void) args; - (void) cfgfile; - cfg = _cfg; - if (NULL != args[0]) - GNUNET_log ( - GNUNET_ERROR_TYPE_WARNING, - _ ("Superfluous command line arguments (starting with `%s') ignored\n"), - args[0]); - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, (void *) cfg); - ns = GNUNET_NAMESTORE_connect (cfg); - if (NULL == ns) - { - fprintf (stderr, _ ("Failed to connect to namestore\n")); - GNUNET_SCHEDULER_shutdown (); - return; - } - idh = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, (void *) cfg); - if (NULL == idh) - { - ret = -1; - fprintf (stderr, _ ("Cannot connect to identity service\n")); - GNUNET_SCHEDULER_shutdown (); - } -} - - - -/** - * The main function for gnunet-namestore. - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, char *const *argv) -{ - int lret; - struct GNUNET_GETOPT_CommandLineOption options[] = - { GNUNET_GETOPT_option_flag ('a', "add", gettext_noop ("add record"), &add), - GNUNET_GETOPT_option_flag ('d', - "delete", - gettext_noop ("delete record"), - &del), - GNUNET_GETOPT_option_flag ('D', - "display", - gettext_noop ("display records"), - &list), - GNUNET_GETOPT_option_flag ('S', - "from-stdin", - gettext_noop ("read commands from stdin"), - &read_from_stdin), - GNUNET_GETOPT_option_string ( - 'e', - "expiration", - "TIME", - gettext_noop ( - "expiration time for record to use (for adding only), \"never\" is possible"), - &expirationstring), - GNUNET_GETOPT_option_string ('i', - "nick", - "NICKNAME", - gettext_noop ( - "set the desired nick name for the zone"), - &nickstring), - GNUNET_GETOPT_option_flag ('m', - "monitor", - gettext_noop ( - "monitor changes in the namestore"), - &monitor), - GNUNET_GETOPT_option_string ('n', - "name", - "NAME", - gettext_noop ( - "name of the record to add/delete/display"), - &name), - GNUNET_GETOPT_option_flag ('r', - "recordline", - gettext_noop ("Output in recordline format"), - &output_recordline), - GNUNET_GETOPT_option_string ('Z', - "zone-to-name", - "KEY", - gettext_noop ( - "determine our name for the given KEY"), - &reverse_pkey), - GNUNET_GETOPT_option_string ('t', - "type", - "TYPE", - gettext_noop ( - "type of the record to add/delete/display"), - &typestring), - GNUNET_GETOPT_option_string ('u', - "uri", - "URI", - gettext_noop ("URI to import into our zone"), - &uri), - GNUNET_GETOPT_option_string ('V', - "value", - "VALUE", - gettext_noop ( - "value of the record to add/delete"), - &value), - GNUNET_GETOPT_option_flag ('p', - "public", - gettext_noop ("create or list public record"), - &is_public), - GNUNET_GETOPT_option_flag ('o', - "omit-private", - gettext_noop ("omit private records"), - &omit_private), - GNUNET_GETOPT_option_flag ('T', - "include-maintenance", - gettext_noop ( - "do not filter maintenance records"), - &include_maintenance), - GNUNET_GETOPT_option_flag ('P', - "purge-orphans", - gettext_noop ( - "purge namestore of all orphans"), - &purge_orphaned), - GNUNET_GETOPT_option_flag ('O', - "list-orphans", - gettext_noop ( - "show private key for orphaned records for recovery using `gnunet-identity -C -P '. Use in combination with --display"), - &list_orphaned), - GNUNET_GETOPT_option_flag ('X', - "purge-zone-records", - gettext_noop ( - "delete all records in specified zone"), - &purge_zone), - GNUNET_GETOPT_option_flag ( - 's', - "shadow", - gettext_noop ( - "create shadow record (only valid if all other records of the same type have expired"), - &is_shadow), - GNUNET_GETOPT_option_string ('z', - "zone", - "EGO", - gettext_noop ( - "name of the ego controlling the zone"), - &ego_name), - GNUNET_GETOPT_OPTION_END }; - - - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - - is_public = -1; - is_shadow = -1; - GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL); - if (GNUNET_OK != - (lret = GNUNET_PROGRAM_run (argc, - argv, - "gnunet-namestore", - _ ("GNUnet zone manipulation tool"), - options, - &run, - NULL))) - { - GNUNET_free_nz ((void *) argv); - // FIXME - // GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey); - return lret; - } - GNUNET_free_nz ((void *) argv); - // FIXME - // GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey); - return ret; -} - - -/* end of gnunet-namestore.c */ diff --git a/src/namestore/gnunet-service-namestore.c b/src/namestore/gnunet-service-namestore.c deleted file mode 100644 index 26de295bf..000000000 --- a/src/namestore/gnunet-service-namestore.c +++ /dev/null @@ -1,2752 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012, 2013, 2014, 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file namestore/gnunet-service-namestore.c - * @brief namestore for the GNUnet naming system - * @author Matthias Wachs - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_gns_service.h" -#include "gnunet_namestore_service.h" -#include "gnunet_namestore_plugin.h" -#include "gnunet_statistics_service.h" -#include "gnunet_signatures.h" -#include "namestore.h" - -#define LOG_STRERROR_FILE(kind, syscall, filename) \ - GNUNET_log_from_strerror_file (kind, "util", syscall, filename) - -/** - * If a monitor takes more than 1 minute to process an event, print a warning. - */ -#define MONITOR_STALL_WARN_DELAY GNUNET_TIME_UNIT_MINUTES - -/** - * Size of the cache used by #get_nick_record() - */ -#define NC_SIZE 16 - -/** - * A namestore client - */ -struct NamestoreClient; - - -/** - * A namestore iteration operation. - */ -struct ZoneIteration -{ - /** - * Next element in the DLL - */ - struct ZoneIteration *next; - - /** - * Previous element in the DLL - */ - struct ZoneIteration *prev; - - /** - * Namestore client which intiated this zone iteration - */ - struct NamestoreClient *nc; - - /** - * The nick to add to the records - */ - struct GNUNET_GNSRECORD_Data *nick; - - /** - * Key of the zone we are iterating over. - */ - struct GNUNET_CRYPTO_PrivateKey zone; - - /** - * The record set filter - */ - enum GNUNET_GNSRECORD_Filter filter; - - /** - * Last sequence number in the zone iteration used to address next - * result of the zone iteration in the store - * - * Initially set to 0. - * Updated in #zone_iterate_proc() - */ - uint64_t seq; - - /** - * The operation id for the zone iteration in the response for the client - */ - uint32_t request_id; - - /** - * Offset of the zone iteration used to address next result of the zone - * iteration in the store - * - * Initially set to 0 in #handle_iteration_start - * Incremented with by every call to #handle_iteration_next - */ - uint32_t offset; - - /** - * Number of pending cache operations triggered by this zone iteration which we - * need to wait for before allowing the client to continue. - */ - unsigned int cache_ops; - - /** - * Set to #GNUNET_YES if the last iteration exhausted the limit set by the - * client and we should send the #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END - * message and free the data structure once @e cache_ops is zero. - */ - int send_end; -}; - -/** - * A namestore client - */ -struct NamestoreClient -{ - /** - * The client - */ - struct GNUNET_SERVICE_Client *client; - - /** - * Database handle for client - */ - struct GNUNET_NAMESTORE_PluginFunctions *GSN_database; - - /** - * Name of loaded plugin (neeed for cleanup) - */ - char *db_lib_name; - - /** - * GNUNET_YES if this nc has begun a transaction which is uncommited. - */ - int in_transaction; - - /** - * Message queue for transmission to @e client - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Head of the DLL of - * Zone iteration operations in progress initiated by this client - */ - struct ZoneIteration *op_head; - - /** - * Tail of the DLL of - * Zone iteration operations in progress initiated by this client - */ - struct ZoneIteration *op_tail; -}; - - -/** - * A namestore monitor. - */ -struct ZoneMonitor -{ - /** - * Next element in the DLL - */ - struct ZoneMonitor *next; - - /** - * Previous element in the DLL - */ - struct ZoneMonitor *prev; - - /** - * Namestore client which intiated this zone monitor - */ - struct NamestoreClient *nc; - - /** - * Private key of the zone. - */ - struct GNUNET_CRYPTO_PrivateKey zone; - - /** - * The record set filter - */ - enum GNUNET_GNSRECORD_Filter filter; - - /** - * Task active during initial iteration. - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Task to warn about slow monitors. - */ - struct GNUNET_SCHEDULER_Task *sa_wait_warning; - - /** - * Since when are we blocked on this monitor? - */ - struct GNUNET_TIME_Absolute sa_waiting_start; - - /** - * Last sequence number in the zone iteration used to address next - * result of the zone iteration in the store - * - * Initially set to 0. - * Updated in #monitor_iterate_cb() - */ - uint64_t seq; - - /** - * Current limit of how many more messages we are allowed - * to queue to this monitor. - */ - uint64_t limit; - - /** - * How many more requests may we receive from the iterator - * before it is at the limit we gave it? Will be below or - * equal to @e limit. The effective limit for monitor - * events is thus @e iteration_cnt - @e limit! - */ - uint64_t iteration_cnt; - - /** - * Are we (still) in the initial iteration pass? - */ - int in_first_iteration; - - /** - * Run again because we skipped an orphan - */ - int run_again; - - /** - * Is there a store activity waiting for this monitor? We only raise the - * flag when it happens and search the DLL for the store activity when we - * had a limit increase. If we cannot find any waiting store activity at - * that time, we clear the flag again. - */ - int sa_waiting; -}; - - - -/** - * Information for an ongoing #handle_record_store() operation. - * Needed as we may wait for monitors to be ready for the notification. - */ -struct StoreActivity -{ - /** - * Kept in a DLL. - */ - struct StoreActivity *next; - - /** - * Kept in a DLL. - */ - struct StoreActivity *prev; - - /** - * Which client triggered the store activity? - */ - struct NamestoreClient *nc; - - /** - * The request ID - */ - uint32_t rid; - - /** - * The currently processed record - */ - uint16_t rd_set_pos; - - /** - * The number of records in this activity - */ - uint16_t rd_set_count; - - /** - * Wheather or not this store action is already commited. - * The store activity will not be processed unless this field is GNUNET_YES - */ - int uncommited; - - /** - * The zone private key - */ - struct GNUNET_CRYPTO_PrivateKey private_key; - - /** - * Copy of the original record set (as data fields in @e rd will - * point into it!). - */ - const struct RecordSet *rs; - - /** - * Next zone monitor that still needs to be notified about this PUT. - */ - struct ZoneMonitor *zm_pos; - -}; - - -/** - * Entry in list of cached nick resolutions. - */ -struct NickCache -{ - /** - * Zone the cache entry is for. - */ - struct GNUNET_CRYPTO_PrivateKey zone; - - /** - * Cached record data. - */ - struct GNUNET_GNSRECORD_Data *rd; - - /** - * Timestamp when this cache entry was used last. - */ - struct GNUNET_TIME_Absolute last_used; -}; - -/** - * We cache nick records to reduce DB load. - */ -static struct NickCache nick_cache[NC_SIZE]; - -/** - * Public key of all zeros. - */ -static const struct GNUNET_CRYPTO_PrivateKey zero; - -/** - * Configuration handle. - */ -static const struct GNUNET_CONFIGURATION_Handle *GSN_cfg; - -/** - * Handle to the statistics service - */ -static struct GNUNET_STATISTICS_Handle *statistics; - -/** - * Name of the database plugin - */ -static char *db_lib_name; - -/** - * Database handle for service - */ -struct GNUNET_NAMESTORE_PluginFunctions *GSN_database; - - -/** - * First active zone monitor. - */ -static struct ZoneMonitor *monitor_head; - -/** - * Last active zone monitor. - */ -static struct ZoneMonitor *monitor_tail; - -/** - * Head of DLL of monitor-blocked store activities. - */ -static struct StoreActivity *sa_head; - -/** - * Tail of DLL of monitor-blocked store activities. - */ -static struct StoreActivity *sa_tail; - -/** - * Notification context shared by all monitors. - */ -static struct GNUNET_NotificationContext *monitor_nc; - -/** - * Returned orphaned records? - */ -static int return_orphaned; - -/** - * Task run during shutdown. - * - * @param cls unused - */ -static void -cleanup_task (void *cls) -{ - (void) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping namestore service\n"); - if (NULL != monitor_nc) - { - GNUNET_notification_context_destroy (monitor_nc); - monitor_nc = NULL; - } - if (NULL != statistics) - { - GNUNET_STATISTICS_destroy (statistics, GNUNET_NO); - statistics = NULL; - } - GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, GSN_database)); - GNUNET_free (db_lib_name); - db_lib_name = NULL; -} - - -/** - * Release memory used by @a sa. - * - * @param sa activity to free - */ -static void -free_store_activity (struct StoreActivity *sa) -{ - GNUNET_CONTAINER_DLL_remove (sa_head, sa_tail, sa); - GNUNET_free (sa); -} - -/** - * Function called with the records for the #GNUNET_GNS_EMPTY_LABEL_AT - * label in the zone. Used to locate the #GNUNET_GNSRECORD_TYPE_NICK - * record, which (if found) is then copied to @a cls for future use. - * - * @param cls a `struct GNUNET_GNSRECORD_Data **` for storing the nick (if found) - * @param seq sequence number of the record, MUST NOT BE ZERO - * @param private_key the private key of the zone (unused) - * @param label should be #GNUNET_GNS_EMPTY_LABEL_AT - * @param rd_count number of records in @a rd - * @param rd records stored under @a label in the zone - */ -static void -lookup_nick_it (void *cls, - uint64_t seq, - const struct GNUNET_CRYPTO_PrivateKey *private_key, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct GNUNET_GNSRECORD_Data **res = cls; - - (void) private_key; - GNUNET_assert (0 != seq); - if (0 != strcmp (label, GNUNET_GNS_EMPTY_LABEL_AT)) - { - GNUNET_break (0); - return; - } - for (unsigned int c = 0; c < rd_count; c++) - { - if (GNUNET_GNSRECORD_TYPE_NICK == rd[c].record_type) - { - (*res) = - GNUNET_malloc (rd[c].data_size + sizeof(struct GNUNET_GNSRECORD_Data)); - (*res)->data = &(*res)[1]; - GNUNET_memcpy ((void *) (*res)->data, rd[c].data, rd[c].data_size); - (*res)->data_size = rd[c].data_size; - (*res)->expiration_time = rd[c].expiration_time; - (*res)->flags = rd[c].flags; - (*res)->record_type = GNUNET_GNSRECORD_TYPE_NICK; - return; - } - } - (*res) = NULL; -} - - -/** - * Add entry to the cache for @a zone and @a nick - * - * @param zone zone key to cache under - * @param nick nick entry to cache - */ -static void -cache_nick (const struct GNUNET_CRYPTO_PrivateKey *zone, - const struct GNUNET_GNSRECORD_Data *nick) -{ - struct NickCache *oldest; - - oldest = NULL; - for (unsigned int i = 0; i < NC_SIZE; i++) - { - struct NickCache *pos = &nick_cache[i]; - - if ((NULL == oldest) || - (oldest->last_used.abs_value_us > pos->last_used.abs_value_us)) - oldest = pos; - if (0 == GNUNET_memcmp (zone, &pos->zone)) - { - oldest = pos; - break; - } - } - GNUNET_free (oldest->rd); - oldest->zone = *zone; - if (NULL != nick) - { - oldest->rd = GNUNET_malloc (sizeof(*nick) + nick->data_size); - *oldest->rd = *nick; - oldest->rd->data = &oldest->rd[1]; - memcpy (&oldest->rd[1], nick->data, nick->data_size); - } - else - { - oldest->rd = NULL; - } - oldest->last_used = GNUNET_TIME_absolute_get (); -} - - -/** - * Return the NICK record for the zone (if it exists). - * - * @param nc the namestore client - * @param zone private key for the zone to look for nick - * @return NULL if no NICK record was found - */ -static struct GNUNET_GNSRECORD_Data * -get_nick_record (const struct GNUNET_CRYPTO_PrivateKey *zone) -{ - struct GNUNET_CRYPTO_PublicKey pub; - struct GNUNET_GNSRECORD_Data *nick; - int res; - - /* check cache first */ - for (unsigned int i = 0; i < NC_SIZE; i++) - { - struct NickCache *pos = &nick_cache[i]; - if ((NULL != pos->rd) && (0 == GNUNET_memcmp (zone, &pos->zone))) - { - if (NULL == pos->rd) - return NULL; - nick = GNUNET_malloc (sizeof(*nick) + pos->rd->data_size); - *nick = *pos->rd; - nick->data = &nick[1]; - memcpy (&nick[1], pos->rd->data, pos->rd->data_size); - pos->last_used = GNUNET_TIME_absolute_get (); - return nick; - } - } - - nick = NULL; - res = GSN_database->lookup_records (GSN_database->cls, - zone, - GNUNET_GNS_EMPTY_LABEL_AT, - &lookup_nick_it, - &nick); - if ((GNUNET_OK != res) || (NULL == nick)) - { -#if ! defined(GNUNET_CULL_LOGGING) - static int do_log = GNUNET_LOG_CALL_STATUS; - - if (0 == do_log) - do_log = GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_DEBUG, - "namestore", - __FILE__, - __FUNCTION__, - __LINE__); - if (1 == do_log) - { - GNUNET_CRYPTO_key_get_public (zone, &pub); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "No nick name set for zone `%s'\n", - GNUNET_GNSRECORD_z2s (&pub)); - } -#endif - /* update cache */ - cache_nick (zone, NULL); - return NULL; - } - - /* update cache */ - cache_nick (zone, nick); - return nick; -} - - -/** - * Merge the nick record @a nick_rd with the rest of the - * record set given in @a rd2. Store the result in @a rdc_res - * and @a rd_res. The @a nick_rd's expiration time is set to - * the maximum expiration time of all of the records in @a rd2. - * - * @param nick_rd the nick record to integrate - * @param rd2_length length of the @a rd2 array - * @param rd2 array of records - * @param[out] rdc_res length of the resulting @a rd_res array - * @param[out] rd_res set to an array of records, - * including @a nick_rd and @a rd2; - * all of the variable-size 'data' fields in @a rd2 are - * allocated in the same chunk of memory! - */ -static void -merge_with_nick_records (const struct GNUNET_GNSRECORD_Data *nick_rd, - unsigned int rd2_length, - const struct GNUNET_GNSRECORD_Data *rd2, - unsigned int *rdc_res, - struct GNUNET_GNSRECORD_Data **rd_res) -{ - uint64_t latest_expiration; - size_t req; - char *data; - size_t data_offset; - struct GNUNET_GNSRECORD_Data *target; - - (*rdc_res) = 1 + rd2_length; - if (0 == 1 + rd2_length) - { - GNUNET_break (0); - (*rd_res) = NULL; - return; - } - req = sizeof(struct GNUNET_GNSRECORD_Data) + nick_rd->data_size; - for (unsigned int i = 0; i < rd2_length; i++) - { - const struct GNUNET_GNSRECORD_Data *orig = &rd2[i]; - - if (req + sizeof(struct GNUNET_GNSRECORD_Data) + orig->data_size < req) - { - GNUNET_break (0); - (*rd_res) = NULL; - return; - } - req += sizeof(struct GNUNET_GNSRECORD_Data) + orig->data_size; - } - target = GNUNET_malloc (req); - (*rd_res) = target; - data = (char *) &target[1 + rd2_length]; - data_offset = 0; - latest_expiration = 0; - for (unsigned int i = 0; i < rd2_length; i++) - { - const struct GNUNET_GNSRECORD_Data *orig = &rd2[i]; - - if (0 != (orig->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) - { - if ((GNUNET_TIME_absolute_get ().abs_value_us + orig->expiration_time) > - latest_expiration) - latest_expiration = orig->expiration_time; - } - else if (orig->expiration_time > latest_expiration) - latest_expiration = orig->expiration_time; - target[i] = *orig; - target[i].data = (void *) &data[data_offset]; - GNUNET_memcpy (&data[data_offset], orig->data, orig->data_size); - data_offset += orig->data_size; - } - /* append nick */ - target[rd2_length] = *nick_rd; - /* Mark as supplemental */ - target[rd2_length].flags = nick_rd->flags | GNUNET_GNSRECORD_RF_SUPPLEMENTAL; - target[rd2_length].expiration_time = latest_expiration; - target[rd2_length].data = (void *) &data[data_offset]; - GNUNET_memcpy (&data[data_offset], nick_rd->data, nick_rd->data_size); - data_offset += nick_rd->data_size; - GNUNET_assert (req == (sizeof(struct GNUNET_GNSRECORD_Data)) * (*rdc_res) - + data_offset); -} - - -/** - * Generate a `struct LookupNameResponseMessage` and send it to the - * given client using the given notification context. - * - * @param nc client to unicast to - * @param request_id request ID to use - * @param zone_key zone key of the zone - * @param name name - * @param rd_count number of records in @a rd - * @param rd array of records - * @param filter record set filter - */ -static int -send_lookup_response_with_filter (struct NamestoreClient *nc, - uint32_t request_id, - const struct - GNUNET_CRYPTO_PrivateKey *zone_key, - const char *name, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd, - enum GNUNET_GNSRECORD_Filter filter) -{ - struct GNUNET_MQ_Envelope *env; - struct RecordResultMessage *zir_msg; - struct GNUNET_GNSRECORD_Data *nick; - struct GNUNET_GNSRECORD_Data *res; - struct GNUNET_GNSRECORD_Data rd_nf[rd_count]; - struct GNUNET_TIME_Absolute block_exp = GNUNET_TIME_UNIT_ZERO_ABS;; - unsigned int res_count; - unsigned int rd_nf_count; - size_t name_len; - size_t key_len; - ssize_t rd_ser_len; - char *name_tmp; - char *rd_ser; - char *emsg; - - nick = get_nick_record (zone_key); - GNUNET_assert (-1 != GNUNET_GNSRECORD_records_get_size (rd_count, rd)); - - if (GNUNET_OK != GNUNET_GNSRECORD_normalize_record_set (name, - rd, - rd_count, - rd_nf, - &rd_nf_count, - &block_exp, - filter, - &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg); - GNUNET_free (emsg); - GNUNET_assert (0); - } - - /** - * FIXME if we ever support GNUNET_NAMESTORE_OMIT_PUBLIC, - * we need to omit adding this public record here - */ - if ((NULL != nick) && (0 != strcmp (name, GNUNET_GNS_EMPTY_LABEL_AT))) - { - nick->flags = - (nick->flags | GNUNET_GNSRECORD_RF_PRIVATE) ^ GNUNET_GNSRECORD_RF_PRIVATE; - merge_with_nick_records (nick, rd_nf_count, rd_nf, &res_count, &res); - } - else - { - res_count = rd_nf_count; - res = (struct GNUNET_GNSRECORD_Data *) rd_nf; - } - if (NULL != nick) - GNUNET_free (nick); - - if (0 == res_count) - { - if (rd_nf != res) - GNUNET_free (res); - return 0; - } - GNUNET_assert (-1 != GNUNET_GNSRECORD_records_get_size (res_count, res)); - - - name_len = strlen (name) + 1; - rd_ser_len = GNUNET_GNSRECORD_records_get_size (res_count, res); - if (rd_ser_len < 0) - { - if (rd_nf != res) - GNUNET_free (res); - GNUNET_break (0); - GNUNET_SERVICE_client_drop (nc->client); - return 0; - } - if (((size_t) rd_ser_len) >= UINT16_MAX - name_len - sizeof(*zir_msg)) - { - if (rd_nf != res) - GNUNET_free (res); - GNUNET_break (0); - GNUNET_SERVICE_client_drop (nc->client); - return 0; - } - key_len = GNUNET_CRYPTO_private_key_get_length (zone_key); - env = GNUNET_MQ_msg_extra (zir_msg, - name_len + rd_ser_len + key_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT); - zir_msg->gns_header.r_id = htonl (request_id); - zir_msg->name_len = htons (name_len); - zir_msg->rd_count = htons (res_count); - zir_msg->rd_len = htons ((uint16_t) rd_ser_len); - zir_msg->key_len = htons (key_len); - GNUNET_CRYPTO_write_private_key_to_buffer (zone_key, - &zir_msg[1], - key_len); - zir_msg->expire = GNUNET_TIME_absolute_hton (block_exp); - name_tmp = (char *) &zir_msg[1] + key_len; - GNUNET_memcpy (name_tmp, name, name_len); - rd_ser = &name_tmp[name_len]; - GNUNET_assert ( - rd_ser_len == - GNUNET_GNSRECORD_records_serialize (res_count, res, rd_ser_len, rd_ser)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending RECORD_RESULT message with %u records\n", - res_count); - GNUNET_STATISTICS_update (statistics, - "Record sets sent to clients", - 1, - GNUNET_NO); - GNUNET_MQ_send (nc->mq, env); - if (rd_nf != res) - GNUNET_free (res); - return res_count; -} - -/** - * Send response to the store request to the client. - * - * @param nc client to talk to - * @param ec status of the operation - * @param rid client's request ID - */ -static void -send_store_response (struct NamestoreClient *nc, - enum GNUNET_ErrorCode ec, - uint32_t rid) -{ - struct GNUNET_MQ_Envelope *env; - struct RecordStoreResponseMessage *rcr_msg; - - GNUNET_assert (NULL != nc); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending RECORD_STORE_RESPONSE message\n"); - GNUNET_STATISTICS_update (statistics, - "Store requests completed", - 1, - GNUNET_NO); - env = GNUNET_MQ_msg (rcr_msg, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE); - rcr_msg->gns_header.r_id = htonl (rid); - rcr_msg->ec = htonl (ec); - GNUNET_MQ_send (nc->mq, env); -} - - -/** - * Function called once we are done with the zone iteration and - * allow the zone iteration client to send us more messages. - * - * @param zi zone iteration we are processing - */ -static void -zone_iteration_done_client_continue (struct ZoneIteration *zi) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_NAMESTORE_Header *em; - - GNUNET_SERVICE_client_continue (zi->nc->client); - if (! zi->send_end) - return; - /* send empty response to indicate end of list */ - env = GNUNET_MQ_msg (em, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END); - em->r_id = htonl (zi->request_id); - GNUNET_MQ_send (zi->nc->mq, env); - - GNUNET_CONTAINER_DLL_remove (zi->nc->op_head, zi->nc->op_tail, zi); - GNUNET_free (zi); -} - - - - -/** - * Print a warning that one of our monitors is no longer reacting. - * - * @param cls a `struct ZoneMonitor` to warn about - */ -static void -warn_monitor_slow (void *cls) -{ - struct ZoneMonitor *zm = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No response from monitor since %s\n", - GNUNET_STRINGS_absolute_time_to_string (zm->sa_waiting_start)); - zm->sa_wait_warning = GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, - &warn_monitor_slow, - zm); -} - - -/** - * Continue processing the @a sa. - * - * @param sa store activity to process - */ -static int -continue_store_activity (struct StoreActivity *sa, - int call_continue) -{ - const struct RecordSet *rd_set = sa->rs; - unsigned int rd_count; - size_t name_len; - size_t rd_ser_len; - const char *name_tmp; - const char *rd_ser; - const char *buf; - char *conv_name; - - // If we are in a transaction, do not notify monitors or update - // cached. This will be done when we are commiting. - if (GNUNET_YES == sa->uncommited) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transaction not yet committed, delaying monitor and cache updates\n"); - send_store_response (sa->nc, GNUNET_EC_NONE, sa->rid); - if (GNUNET_YES == call_continue) - GNUNET_SERVICE_client_continue (sa->nc->client); - return GNUNET_OK; - } - buf = (const char *) &sa[1]; - for (int i = sa->rd_set_pos; i < sa->rd_set_count; i++) - { - rd_set = (struct RecordSet *) buf; - name_len = ntohs (rd_set->name_len); - rd_count = ntohs (rd_set->rd_count); - rd_ser_len = ntohs (rd_set->rd_len); - name_tmp = (const char *) &rd_set[1]; - rd_ser = &name_tmp[name_len]; - conv_name = GNUNET_GNSRECORD_string_normalize (name_tmp); - GNUNET_assert (NULL != conv_name); - { - struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; - - /* We did this before, must succeed again */ - GNUNET_assert ( - GNUNET_OK == - GNUNET_GNSRECORD_records_deserialize (rd_ser_len, rd_ser, rd_count, - rd)); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Checking monitors watching for `%s'\n", - conv_name); - for (struct ZoneMonitor *zm = sa->zm_pos; NULL != zm; zm = sa->zm_pos) - { - if ((0 != GNUNET_memcmp (&sa->private_key, &zm->zone)) && - (0 != GNUNET_memcmp (&zm->zone, &zero))) - { - sa->zm_pos = zm->next; /* not interesting to this monitor */ - continue; - } - if (zm->limit == zm->iteration_cnt) - { - zm->sa_waiting = GNUNET_YES; - zm->sa_waiting_start = GNUNET_TIME_absolute_get (); - if (NULL != zm->sa_wait_warning) - GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); - zm->sa_wait_warning = - GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, - &warn_monitor_slow, - zm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Monitor is blocking client for `%s'\n", - conv_name); - GNUNET_free (conv_name); - return GNUNET_NO; /* blocked on zone monitor */ - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Notifying monitor about changes under label `%s'\n", - conv_name); - if (0 < send_lookup_response_with_filter (zm->nc, - 0, - &sa->private_key, - conv_name, - rd_count, - rd, - zm->filter)) - zm->limit--; - sa->zm_pos = zm->next; - } - sa->rd_set_pos++; - GNUNET_free (conv_name); - } - } - if (GNUNET_YES == call_continue) - GNUNET_SERVICE_client_continue (sa->nc->client); - send_store_response (sa->nc, GNUNET_EC_NONE, sa->rid); - free_store_activity (sa); - return GNUNET_OK; -} - - -/** - * Called whenever a client is disconnected. - * Frees our resources associated with that client. - * - * @param cls closure - * @param client identification of the client - * @param app_ctx the `struct NamestoreClient` of @a client - */ -static void -client_disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - void *app_ctx) -{ - struct NamestoreClient *nc = app_ctx; - struct ZoneIteration *no; - struct StoreActivity *sa = sa_head; - struct StoreActivity *sn; - char *emsg; - - (void) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p disconnected\n", client); - if (GNUNET_YES == nc->in_transaction) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Client in transaction, rolling back...\n"); - if (GNUNET_SYSERR == nc->GSN_database->transaction_rollback ( - nc->GSN_database->cls, - &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to roll back: %s\n", emsg); - GNUNET_free (emsg); - } - else - { - nc->in_transaction = GNUNET_NO; - while (NULL != sa) - { - if ((nc != sa->nc) || - (GNUNET_NO == sa->uncommited)) - { - sa = sa->next; - continue; - } - sn = sa->next; - free_store_activity (sa); - sa = sn; - } - } - } - for (struct ZoneMonitor *zm = monitor_head; NULL != zm; zm = zm->next) - { - if (nc != zm->nc) - continue; - GNUNET_CONTAINER_DLL_remove (monitor_head, monitor_tail, zm); - if (NULL != zm->task) - { - GNUNET_SCHEDULER_cancel (zm->task); - zm->task = NULL; - } - if (NULL != zm->sa_wait_warning) - { - GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); - zm->sa_wait_warning = NULL; - } - for (sa = sa_head; NULL != sa; sa = sn) - { - sn = sa->next; - if (zm == sa->zm_pos) - { - sa->zm_pos = zm->next; - /* this may free sa */ - continue_store_activity (sa, GNUNET_YES); - } - } - GNUNET_free (zm); - break; - } - sa = sa_head; - while (NULL != sa) - { - if (nc != sa->nc) - { - sa = sa->next; - continue; - } - sn = sa->next; - free_store_activity (sa); - sa = sn; - } - while (NULL != (no = nc->op_head)) - { - GNUNET_CONTAINER_DLL_remove (nc->op_head, nc->op_tail, no); - GNUNET_free (no); - } - GNUNET_break (NULL == GNUNET_PLUGIN_unload (nc->db_lib_name, - nc->GSN_database)); - GNUNET_free (nc->db_lib_name); - GNUNET_free (nc); -} - - -/** - * Add a client to our list of active clients. - * - * @param cls NULL - * @param client client to add - * @param mq message queue for @a client - * @return internal namestore client structure for this client - */ -static void * -client_connect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - struct GNUNET_MQ_Handle *mq) -{ - struct NamestoreClient *nc; - char *database; - - (void) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected\n", client); - nc = GNUNET_new (struct NamestoreClient); - nc->client = client; - nc->mq = mq; - /* Loading database plugin */ - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (GSN_cfg, - "namestore", - "database", - &database)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n"); - GNUNET_free (nc); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading %s\n", db_lib_name); - nc->GSN_database = GNUNET_PLUGIN_load (db_lib_name, (void *) GSN_cfg); - GNUNET_free (database); - if (NULL == nc->GSN_database) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not load database backend `%s'\n", - db_lib_name); - GNUNET_free (nc); - return NULL; - } - nc->db_lib_name = GNUNET_strdup (db_lib_name); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded %s\n", db_lib_name); - return nc; -} - - -/** - * Closure for #lookup_it(). - */ -struct RecordLookupContext -{ - /** - * The label to look up. - */ - const char *label; - - /** - * The record result. - */ - char *res_rd; - - /** - * The nick for the zone. - */ - struct GNUNET_GNSRECORD_Data *nick; - - /** - * If a record set was found or not. - */ - int found; - - /** - * The record filter - */ - enum GNUNET_GNSRECORD_Filter filter; - - /** - * The number of found records. - */ - unsigned int res_rd_count; - - /** - * The length of the serialized records. - */ - ssize_t rd_ser_len; -}; - - -/** - * Function called by the namestore plugin when we are trying to lookup - * a record as part of #handle_record_lookup(). Merges all results into - * the context. - * - * @param cls closure with a `struct RecordLookupContext` - * @param seq unique serial number of the record, MUST NOT BE ZERO - * @param private_key private key of the zone - * @param label name that is being mapped (at most 255 characters long) - * @param rd_count number of entries in @a rd array - * @param rd array of records with data to store - */ -static void -lookup_it (void *cls, - uint64_t seq, - const struct GNUNET_CRYPTO_PrivateKey *private_key, - const char *label, - unsigned int rd_count_nf, - const struct GNUNET_GNSRECORD_Data *rd_nf) -{ - struct RecordLookupContext *rlc = cls; - struct GNUNET_GNSRECORD_Data rd[rd_count_nf]; - struct GNUNET_TIME_Absolute block_exp; - unsigned int rd_count = 0; - char *emsg; - - (void) private_key; - GNUNET_assert (0 != seq); - if (0 != strcmp (label, rlc->label)) - return; - rlc->found = GNUNET_YES; - - if (GNUNET_OK != GNUNET_GNSRECORD_normalize_record_set (rlc->label, - rd_nf, - rd_count_nf, - rd, - &rd_count, - &block_exp, - rlc->filter, - &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg); - GNUNET_free (emsg); - GNUNET_assert (0); - } - - if (0 == rd_count) - { - rlc->rd_ser_len = 0; - rlc->res_rd_count = 0; - rlc->res_rd = NULL; - return; - } - if ((NULL != rlc->nick) && (0 != strcmp (label, GNUNET_GNS_EMPTY_LABEL_AT))) - { - /* Merge */ - struct GNUNET_GNSRECORD_Data *rd_res; - unsigned int rdc_res; - - rd_res = NULL; - rdc_res = 0; - rlc->nick->flags = (rlc->nick->flags | GNUNET_GNSRECORD_RF_PRIVATE) - ^ GNUNET_GNSRECORD_RF_PRIVATE; - merge_with_nick_records (rlc->nick, rd_count, rd, &rdc_res, &rd_res); - rlc->rd_ser_len = GNUNET_GNSRECORD_records_get_size (rdc_res, rd_res); - if (rlc->rd_ser_len < 0) - { - GNUNET_break (0); - GNUNET_free (rd_res); - rlc->found = GNUNET_NO; - rlc->rd_ser_len = 0; - return; - } - rlc->res_rd_count = rdc_res; - rlc->res_rd = GNUNET_malloc (rlc->rd_ser_len); - if (rlc->rd_ser_len != GNUNET_GNSRECORD_records_serialize (rdc_res, - rd_res, - rlc->rd_ser_len, - rlc->res_rd)) - { - GNUNET_break (0); - GNUNET_free (rlc->res_rd); - rlc->res_rd = NULL; - rlc->res_rd_count = 0; - rlc->rd_ser_len = 0; - GNUNET_free (rd_res); - rlc->found = GNUNET_NO; - return; - } - GNUNET_free (rd_res); - GNUNET_free (rlc->nick); - rlc->nick = NULL; - } - else - { - rlc->rd_ser_len = GNUNET_GNSRECORD_records_get_size (rd_count, rd); - if (rlc->rd_ser_len < 0) - { - GNUNET_break (0); - rlc->found = GNUNET_NO; - rlc->rd_ser_len = 0; - return; - } - rlc->res_rd_count = rd_count; - rlc->res_rd = GNUNET_malloc (rlc->rd_ser_len); - if (rlc->rd_ser_len != GNUNET_GNSRECORD_records_serialize (rd_count, - rd, - rlc->rd_ser_len, - rlc->res_rd)) - { - GNUNET_break (0); - GNUNET_free (rlc->res_rd); - rlc->res_rd = NULL; - rlc->res_rd_count = 0; - rlc->rd_ser_len = 0; - rlc->found = GNUNET_NO; - return; - } - } -} - - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP message - * - * @param cls client sending the message - * @param ll_msg message of type `struct LabelLookupMessage` - * @return #GNUNET_OK if @a ll_msg is well-formed - */ -static int -check_record_lookup (void *cls, const struct LabelLookupMessage *ll_msg) -{ - uint32_t name_len; - size_t src_size; - size_t key_len; - - (void) cls; - name_len = ntohs (ll_msg->label_len); - key_len = ntohs (ll_msg->key_len); - src_size = ntohs (ll_msg->gns_header.header.size); - if (name_len + key_len != src_size - sizeof(struct LabelLookupMessage)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP message - * - * @param cls client sending the message - * @param ll_msg message of type `struct LabelLookupMessage` - */ -static void -handle_record_lookup (void *cls, const struct LabelLookupMessage *ll_msg) -{ - struct GNUNET_CRYPTO_PrivateKey zone; - struct NamestoreClient *nc = cls; - struct GNUNET_MQ_Envelope *env; - struct LabelLookupResponseMessage *llr_msg; - struct RecordLookupContext rlc; - const char *name_tmp; - char *res_name; - char *conv_name; - uint32_t name_len; - int res; - size_t key_len; - size_t kb_read; - - key_len = ntohs (ll_msg->key_len); - if ((GNUNET_SYSERR == - GNUNET_CRYPTO_read_private_key_from_buffer (&ll_msg[1], - key_len, - &zone, - &kb_read)) || - (kb_read != key_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error reading private key\n"); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - name_tmp = (const char *) &ll_msg[1] + key_len; - GNUNET_SERVICE_client_continue (nc->client); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received NAMESTORE_RECORD_LOOKUP message for name `%s'\n", - name_tmp); - - conv_name = GNUNET_GNSRECORD_string_normalize (name_tmp); - if (NULL == conv_name) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error converting name `%s'\n", - name_tmp); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - name_len = strlen (conv_name) + 1; - rlc.label = conv_name; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Looking up with filter %u\n", ntohs (ll_msg->filter)); - rlc.filter = ntohs (ll_msg->filter); - rlc.found = GNUNET_NO; - rlc.res_rd_count = 0; - rlc.res_rd = NULL; - rlc.rd_ser_len = 0; - rlc.nick = get_nick_record (&zone); - if (GNUNET_YES != ntohs (ll_msg->is_edit_request)) - res = nc->GSN_database->lookup_records (nc->GSN_database->cls, - &zone, - conv_name, - &lookup_it, - &rlc); - else - res = nc->GSN_database->edit_records (nc->GSN_database->cls, - &zone, - conv_name, - &lookup_it, - &rlc); - - env = - GNUNET_MQ_msg_extra (llr_msg, - key_len + name_len + rlc.rd_ser_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE); - llr_msg->gns_header.r_id = ll_msg->gns_header.r_id; - GNUNET_memcpy (&llr_msg[1], &ll_msg[1], key_len); - llr_msg->key_len = ll_msg->key_len; - llr_msg->name_len = htons (name_len); - llr_msg->rd_count = htons (rlc.res_rd_count); - llr_msg->rd_len = htons (rlc.rd_ser_len); - llr_msg->reserved = htons (0); - res_name = ((char *) &llr_msg[1]) + key_len; - if (GNUNET_YES == rlc.found) - llr_msg->found = htons (GNUNET_YES); - else if (GNUNET_SYSERR == res) - llr_msg->found = htons (GNUNET_SYSERR); - else - llr_msg->found = htons (GNUNET_NO); - GNUNET_memcpy (res_name, conv_name, name_len); - GNUNET_memcpy (&res_name[name_len], rlc.res_rd, rlc.rd_ser_len); - GNUNET_MQ_send (nc->mq, env); - GNUNET_free (rlc.res_rd); - GNUNET_free (conv_name); -} - - - -/** - * Checks a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE message - * - * @param cls client sending the message - * @param rp_msg message of type `struct RecordStoreMessage` - * @return #GNUNET_OK if @a rp_msg is well-formed - */ -static int -check_record_store (void *cls, const struct RecordStoreMessage *rp_msg) -{ - size_t msg_size; - size_t min_size_exp; - size_t rd_set_count; - size_t key_len; - - (void) cls; - msg_size = ntohs (rp_msg->gns_header.header.size); - rd_set_count = ntohs (rp_msg->rd_set_count); - key_len = ntohs (rp_msg->key_len); - - min_size_exp = sizeof(*rp_msg) + key_len + sizeof (struct RecordSet) - * rd_set_count; - if (msg_size < min_size_exp) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - -struct LookupExistingRecordsContext -{ - - /** - * The expiration of the existing records or tombstone - */ - struct GNUNET_TIME_Absolute exp; - - /** - * Whether the existing record set consists only of a tombstone - * (e.g. is "empty") - */ - int only_tombstone; - -}; - - -/** - * Check if set contains a tombstone, store if necessary - * - * @param cls a `struct GNUNET_GNSRECORD_Data **` for storing the nick (if found) - * @param seq sequence number of the record, MUST NOT BE ZERO - * @param private_key the private key of the zone (unused) - * @param label should be #GNUNET_GNS_EMPTY_LABEL_AT - * @param rd_count number of records in @a rd - * @param rd records stored under @a label in the zone - */ -static void -get_existing_rd_exp (void *cls, - uint64_t seq, - const struct - GNUNET_CRYPTO_PrivateKey *private_key, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct LookupExistingRecordsContext *lctx = cls; - struct GNUNET_GNSRECORD_Data rd_pub[rd_count]; - unsigned int rd_pub_count; - char *emsg; - - if ((1 == rd_count) && - (GNUNET_GNSRECORD_TYPE_TOMBSTONE == rd[0].record_type)) - { - /* This record set contains only a tombstone! */ - lctx->only_tombstone = GNUNET_YES; - } - if (GNUNET_OK != - GNUNET_GNSRECORD_normalize_record_set (label, - rd, - rd_count, - rd_pub, - &rd_pub_count, - &lctx->exp, - GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE, - &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%s\n", emsg); - GNUNET_free (emsg); - } -} - -static enum GNUNET_ErrorCode -store_record_set (struct NamestoreClient *nc, - const struct GNUNET_CRYPTO_PrivateKey *private_key, - const struct RecordSet *rd_set, - ssize_t *len) -{ - size_t name_len; - size_t rd_ser_len; - const char *name_tmp; - const char *rd_ser; - char *conv_name; - char *emsg; - unsigned int rd_count; - int res; - enum GNUNET_ErrorCode ec; - struct GNUNET_TIME_Absolute new_block_exp; - struct LookupExistingRecordsContext lctx; - *len = sizeof (struct RecordSet); - - ec = GNUNET_EC_NONE; - lctx.only_tombstone = GNUNET_NO; - lctx.exp = GNUNET_TIME_UNIT_ZERO_ABS; - new_block_exp = GNUNET_TIME_UNIT_ZERO_ABS; - name_len = ntohs (rd_set->name_len); - *len += name_len; - rd_count = ntohs (rd_set->rd_count); - rd_ser_len = ntohs (rd_set->rd_len); - *len += rd_ser_len; - name_tmp = (const char *) &rd_set[1]; - rd_ser = &name_tmp[name_len]; - { - struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; - - /* Extracting and converting private key */ - conv_name = GNUNET_GNSRECORD_string_normalize (name_tmp); - if (NULL == conv_name) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error normalizing name `%s'\n", - name_tmp); - return GNUNET_EC_NAMESTORE_LABEL_INVALID; - } - - /* Check name for validity */ - if (GNUNET_OK != GNUNET_GNSRECORD_label_check (conv_name, &emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Label invalid: `%s'\n", - emsg); - GNUNET_free (emsg); - GNUNET_free (conv_name); - return GNUNET_EC_NAMESTORE_LABEL_INVALID; - } - - if (GNUNET_OK != - GNUNET_GNSRECORD_records_deserialize (rd_ser_len, rd_ser, rd_count, - rd)) - { - GNUNET_free (conv_name); - return GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; - } - - GNUNET_STATISTICS_update (statistics, - "Well-formed store requests received", - 1, - GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating %u records for name `%s'\n", - (unsigned int) rd_count, - conv_name); - if ((GNUNET_NO == nc->GSN_database->lookup_records (nc->GSN_database->cls, - private_key, - conv_name, - &get_existing_rd_exp, - &lctx)) - && - (rd_count == 0)) - { - /* This name does not exist, so cannot be removed */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name `%s' does not exist, no deletion required\n", - conv_name); - res = GNUNET_NO; - ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND; - } - else - { - /* remove "NICK" records, unless this is for the - #GNUNET_GNS_EMPTY_LABEL_AT label - We may need one additional record later for tombstone. - FIXME: Since we must normalize the record set (check for - consistency etc) we have to iterate the set twice. - May be inefficient. - We cannot really move the nick caching into GNSRECORD. - */ - struct GNUNET_GNSRECORD_Data rd_clean[GNUNET_NZL (rd_count)]; - struct GNUNET_GNSRECORD_Data rd_nf[GNUNET_NZL (rd_count) + 1]; - unsigned int rd_clean_off; - unsigned int rd_nf_count; - int have_nick; - - rd_clean_off = 0; - have_nick = GNUNET_NO; - for (unsigned int i = 0; i < rd_count; i++) - { - rd_clean[rd_clean_off] = rd[i]; - - if ((0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, conv_name)) || - (GNUNET_GNSRECORD_TYPE_NICK != rd[i].record_type)) - rd_clean_off++; - - if ((0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, conv_name)) && - (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type)) - { - // FIXME: In case this is an uncommited transaction, - // we should not do this here. Can we do this in the store activity? - cache_nick (private_key, &rd[i]); - have_nick = GNUNET_YES; - } - } - if (GNUNET_OK != - GNUNET_GNSRECORD_normalize_record_set (conv_name, - rd_clean, - rd_clean_off, - rd_nf, - &rd_nf_count, - &new_block_exp, - GNUNET_GNSRECORD_FILTER_NONE, - &emsg)) - { - GNUNET_free (conv_name); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Error normalizing record set: `%s'\n", - emsg); - GNUNET_free (emsg); - return GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "%u/%u records before tombstone\n", rd_nf_count, - rd_clean_off); - /* - * If existing_block_exp is 0, then there was no record set - * and no tombstone. - * Otherwise, if the existing block expiration is after the - * new block expiration would be, we need to add a tombstone - * or update it. - */ - if (GNUNET_TIME_absolute_cmp (new_block_exp, <=, lctx.exp)) - { - rd_nf[rd_nf_count].record_type = GNUNET_GNSRECORD_TYPE_TOMBSTONE; - rd_nf[rd_nf_count].expiration_time = - lctx.exp.abs_value_us; - rd_nf[rd_nf_count].data = NULL; - rd_nf[rd_nf_count].data_size = 0; - rd_nf[rd_nf_count].flags = GNUNET_GNSRECORD_RF_PRIVATE; - rd_nf_count++; - } - if ((0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, conv_name)) && - (GNUNET_NO == have_nick)) - { - /* remove nick record from cache, in case we have one there */ - // FIXME: In case this is an uncommited transaction, - // we should not do this here. Can we do this in the store activity? - cache_nick (private_key, NULL); - } - res = nc->GSN_database->store_records (nc->GSN_database->cls, - private_key, - conv_name, - rd_nf_count, - rd_nf); - /* If after a store there is only a TOMBSTONE left, and - * there was >1 record under this label found (the tombstone; indicated - * through res != GNUNET_NO) then we should return "NOT FOUND" == GNUNET_NO - */ - if ((GNUNET_SYSERR != res) && - (0 == rd_count) && - (1 == rd_nf_count) && - (GNUNET_GNSRECORD_TYPE_TOMBSTONE == rd_nf[0].record_type) && - (GNUNET_YES == lctx.only_tombstone)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client tried to remove non-existant record\n"); - ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND; - } - } - - if (GNUNET_SYSERR == res) - { - /* store not successful, no need to tell monitors */ - GNUNET_free (conv_name); - return GNUNET_EC_NAMESTORE_STORE_FAILED; - } - } - GNUNET_free (conv_name); - return ec; -} - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE message - * - * @param cls client sending the message - * @param rp_msg message of type `struct RecordStoreMessage` - */ -static void -handle_record_store (void *cls, const struct RecordStoreMessage *rp_msg) -{ - struct GNUNET_CRYPTO_PrivateKey zone; - struct NamestoreClient *nc = cls; - uint32_t rid; - uint16_t rd_set_count; - const char *buf; - ssize_t read; - size_t key_len; - size_t kb_read; - size_t rp_msg_len; - size_t rs_len; - size_t rs_off; - struct StoreActivity *sa; - struct RecordSet *rs; - enum GNUNET_ErrorCode res; - - key_len = ntohs (rp_msg->key_len); - rp_msg_len = ntohs (rp_msg->gns_header.header.size); - rs_off = sizeof (*rp_msg) + key_len; - rs_len = rp_msg_len - rs_off; - if ((GNUNET_SYSERR == - GNUNET_CRYPTO_read_private_key_from_buffer (&rp_msg[1], - key_len, - &zone, - &kb_read)) || - (kb_read != key_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error reading private key\n"); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received NAMESTORE_RECORD_STORE message\n"); - rid = ntohl (rp_msg->gns_header.r_id); - rd_set_count = ntohs (rp_msg->rd_set_count); - buf = (const char *) rp_msg + rs_off; - for (int i = 0; i < rd_set_count; i++) - { - rs = (struct RecordSet *) buf; - res = store_record_set (nc, &zone, - rs, &read); - if (GNUNET_EC_NONE != res) - { - send_store_response (nc, res, rid); - GNUNET_SERVICE_client_continue (nc->client); - return; - } - buf += read; - } - sa = GNUNET_malloc (sizeof(struct StoreActivity) + rs_len); - GNUNET_CONTAINER_DLL_insert (sa_head, sa_tail, sa); - sa->nc = nc; - sa->rs = (struct RecordSet *) &sa[1]; - sa->rd_set_count = rd_set_count; - GNUNET_memcpy (&sa[1], (char *) rp_msg + rs_off, rs_len); - sa->rid = rid; - sa->rd_set_pos = 0; - sa->private_key = zone; - sa->zm_pos = monitor_head; - sa->uncommited = nc->in_transaction; - continue_store_activity (sa, GNUNET_YES); -} - -static void -send_tx_response (int rid, enum GNUNET_ErrorCode ec, struct NamestoreClient *nc) -{ - struct TxControlResultMessage *txr_msg; - struct GNUNET_MQ_Envelope *env; - - env = - GNUNET_MQ_msg (txr_msg, GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL_RESULT); - txr_msg->gns_header.r_id = rid; - txr_msg->ec = htonl (ec); - GNUNET_MQ_send (nc->mq, env); -} - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL message - * - * @param cls client sending the message - * @param tx_msg message of type `struct TxControlMessage` - */ -static void -handle_tx_control (void *cls, const struct TxControlMessage *tx_msg) -{ - struct NamestoreClient *nc = cls; - struct StoreActivity *sa = sa_head; - struct StoreActivity *sn; - enum GNUNET_GenericReturnValue ret; - char *emsg = NULL; - int blocked = GNUNET_NO; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received TX_CONTROL message\n"); - - switch (ntohs (tx_msg->control)) - { - case GNUNET_NAMESTORE_TX_BEGIN: - ret = nc->GSN_database->transaction_begin (nc->GSN_database->cls, - &emsg); - send_tx_response (tx_msg->gns_header.r_id, - (GNUNET_SYSERR == ret) ? - GNUNET_EC_NAMESTORE_BACKEND_FAILED : GNUNET_EC_NONE, nc); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Databse backend error: `%s'", emsg); - GNUNET_free (emsg); - } - GNUNET_SERVICE_client_continue (nc->client); - nc->in_transaction = GNUNET_YES; - break; - case GNUNET_NAMESTORE_TX_COMMIT: - ret = nc->GSN_database->transaction_commit (nc->GSN_database->cls, - &emsg); - send_tx_response (tx_msg->gns_header.r_id, - (GNUNET_SYSERR == ret) ? - GNUNET_EC_NAMESTORE_BACKEND_FAILED : GNUNET_EC_NONE, - nc); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Databse backend error: `%s'", emsg); - GNUNET_free (emsg); - } - if (GNUNET_SYSERR != ret) - { - nc->in_transaction = GNUNET_NO; - while (NULL != sa) - { - if ((nc != sa->nc) || - (GNUNET_NO == sa->uncommited)) - { - sa = sa->next; - continue; - } - sa->uncommited = GNUNET_NO; - sn = sa->next; - if (GNUNET_OK != continue_store_activity (sa, GNUNET_NO)) - blocked = GNUNET_YES; - sa = sn; - } - if (GNUNET_YES != blocked) - GNUNET_SERVICE_client_continue (nc->client); - } - break; - case GNUNET_NAMESTORE_TX_ROLLBACK: - ret = nc->GSN_database->transaction_rollback (nc->GSN_database->cls, - &emsg); - send_tx_response (tx_msg->gns_header.r_id, - (GNUNET_SYSERR == ret) ? - GNUNET_EC_NAMESTORE_BACKEND_FAILED : GNUNET_EC_NONE, nc); - GNUNET_SERVICE_client_continue (nc->client); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Databse backend error: `%s'", emsg); - GNUNET_free (emsg); - } - if (GNUNET_SYSERR != ret) - { - nc->in_transaction = GNUNET_NO; - while (NULL != sa) - { - if ((nc != sa->nc) || - (GNUNET_NO == sa->uncommited)) - { - sa = sa->next; - continue; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Discarding uncommited StoreActivity\n"); - sn = sa->next; - free_store_activity (sa); - sa = sn; - } - } - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unknown control type %u\n", ntohs (tx_msg->control)); - GNUNET_break (0); - } -} - -/** - * Context for record remove operations passed from #handle_zone_to_name to - * #handle_zone_to_name_it as closure - */ -struct ZoneToNameCtx -{ - /** - * Namestore client - */ - struct NamestoreClient *nc; - - /** - * Request id (to be used in the response to the client). - */ - uint32_t rid; - - /** - * Set to #GNUNET_OK on success, #GNUNET_SYSERR on error. Note that - * not finding a name for the zone still counts as a 'success' here, - * as this field is about the success of executing the IPC protocol. - */ - enum GNUNET_ErrorCode ec; -}; - - -/** - * Zone to name iterator - * - * @param cls struct ZoneToNameCtx * - * @param seq sequence number of the record, MUST NOT BE ZERO - * @param zone_key the zone key - * @param name name - * @param rd_count number of records in @a rd - * @param rd record data - */ -static void -handle_zone_to_name_it (void *cls, - uint64_t seq, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *name, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct ZoneToNameCtx *ztn_ctx = cls; - struct GNUNET_MQ_Envelope *env; - struct ZoneToNameResponseMessage *ztnr_msg; - size_t name_len; - size_t key_len; - ssize_t rd_ser_len; - size_t msg_size; - char *name_tmp; - char *rd_tmp; - - GNUNET_assert (0 != seq); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found result for zone-to-name lookup: `%s'\n", - name); - ztn_ctx->ec = GNUNET_EC_NONE; - name_len = (NULL == name) ? 0 : strlen (name) + 1; - rd_ser_len = GNUNET_GNSRECORD_records_get_size (rd_count, rd); - if (rd_ser_len < 0) - { - GNUNET_break (0); - ztn_ctx->ec = htonl (GNUNET_EC_NAMESTORE_UNKNOWN); - return; - } - key_len = GNUNET_CRYPTO_private_key_get_length (zone_key); - msg_size = sizeof(struct ZoneToNameResponseMessage) - + name_len + rd_ser_len + key_len; - if (msg_size >= GNUNET_MAX_MESSAGE_SIZE) - { - GNUNET_break (0); - ztn_ctx->ec = GNUNET_EC_NAMESTORE_RECORD_TOO_BIG; - return; - } - env = - GNUNET_MQ_msg_extra (ztnr_msg, - key_len + name_len + rd_ser_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE); - ztnr_msg->gns_header.header.size = htons (msg_size); - ztnr_msg->gns_header.r_id = htonl (ztn_ctx->rid); - ztnr_msg->ec = htonl (ztn_ctx->ec); - ztnr_msg->rd_len = htons (rd_ser_len); - ztnr_msg->rd_count = htons (rd_count); - ztnr_msg->name_len = htons (name_len); - ztnr_msg->key_len = htons (key_len); - GNUNET_CRYPTO_write_private_key_to_buffer (zone_key, - &ztnr_msg[1], - key_len); - name_tmp = (char *) &ztnr_msg[1] + key_len; - GNUNET_memcpy (name_tmp, name, name_len); - rd_tmp = &name_tmp[name_len]; - GNUNET_assert ( - rd_ser_len == - GNUNET_GNSRECORD_records_serialize (rd_count, rd, rd_ser_len, rd_tmp)); - ztn_ctx->ec = GNUNET_EC_NONE; - GNUNET_MQ_send (ztn_ctx->nc->mq, env); -} - -static enum GNUNET_GenericReturnValue -check_zone_to_name (void *cls, - const struct ZoneToNameMessage *zis_msg) -{ - return GNUNET_OK; -} - - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME message - * - * @param cls client client sending the message - * @param ztn_msg message of type 'struct ZoneToNameMessage' - */ -static void -handle_zone_to_name (void *cls, const struct ZoneToNameMessage *ztn_msg) -{ - struct GNUNET_CRYPTO_PrivateKey zone; - struct GNUNET_CRYPTO_PublicKey value_zone; - struct NamestoreClient *nc = cls; - struct ZoneToNameCtx ztn_ctx; - struct GNUNET_MQ_Envelope *env; - struct ZoneToNameResponseMessage *ztnr_msg; - size_t key_len; - size_t pkey_len; - size_t kb_read; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ZONE_TO_NAME message\n"); - ztn_ctx.rid = ntohl (ztn_msg->gns_header.r_id); - ztn_ctx.nc = nc; - ztn_ctx.ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - key_len = ntohs (ztn_msg->key_len); - if ((GNUNET_SYSERR == - GNUNET_CRYPTO_read_private_key_from_buffer (&ztn_msg[1], - key_len, - &zone, - &kb_read)) || - (kb_read != key_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Error parsing private key.\n"); - GNUNET_SERVICE_client_drop (nc->client); - GNUNET_break (0); - return; - } - pkey_len = ntohs (ztn_msg->pkey_len); - if ((GNUNET_SYSERR == - GNUNET_CRYPTO_read_public_key_from_buffer ((char*) &ztn_msg[1] - + key_len, - pkey_len, - &value_zone, - &kb_read)) || - (kb_read != pkey_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Error parsing public key.\n"); - GNUNET_SERVICE_client_drop (nc->client); - GNUNET_break (0); - return; - } - if (GNUNET_SYSERR == nc->GSN_database->zone_to_name (nc->GSN_database->cls, - &zone, - &value_zone, - &handle_zone_to_name_it, - &ztn_ctx)) - { - /* internal error, hang up instead of signalling something - that might be wrong */ - GNUNET_break (0); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - if (GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND == ztn_ctx.ec) - { - /* no result found, send empty response */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Found no result for zone-to-name lookup.\n"); - env = GNUNET_MQ_msg (ztnr_msg, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE); - ztnr_msg->gns_header.r_id = ztn_msg->gns_header.r_id; - ztnr_msg->ec = htonl (GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND); - GNUNET_MQ_send (nc->mq, env); - } - GNUNET_SERVICE_client_continue (nc->client); -} - - -/** - * Context for record remove operations passed from - * #run_zone_iteration_round to #zone_iterate_proc as closure - */ -struct ZoneIterationProcResult -{ - /** - * The zone iteration handle - */ - struct ZoneIteration *zi; - - /** - * Number of results left to be returned in this iteration. - */ - uint64_t limit; - - /** - * Skip a result and run again unless GNUNET_NO - */ - int run_again; -}; - - -/** - * Process results for zone iteration from database - * - * @param cls struct ZoneIterationProcResult - * @param seq sequence number of the record, MUST NOT BE ZERO - * @param zone_key the zone key - * @param name name - * @param rd_count number of records for this name - * @param rd record data - */ -static void -zone_iterate_proc (void *cls, - uint64_t seq, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *name, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct ZoneIterationProcResult *proc = cls; - - GNUNET_assert (0 != seq); - if ((NULL == zone_key) && (NULL == name)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration done\n"); - return; - } - if ((NULL == zone_key) || (NULL == name)) - { - /* what is this!? should never happen */ - GNUNET_break (0); - return; - } - if (0 == proc->limit) - { - /* what is this!? should never happen */ - GNUNET_break (0); - return; - } - proc->zi->seq = seq; - if (0 < send_lookup_response_with_filter (proc->zi->nc, - proc->zi->request_id, - zone_key, - name, - rd_count, - rd, - proc->zi->filter)) - proc->limit--; - else - proc->run_again = GNUNET_YES; -} - - -/** - * Perform the next round of the zone iteration. - * - * @param zi zone iterator to process - * @param limit number of results to return in one pass - */ -static void -run_zone_iteration_round (struct ZoneIteration *zi, uint64_t limit) -{ - struct ZoneIterationProcResult proc; - struct GNUNET_TIME_Absolute start; - struct GNUNET_TIME_Relative duration; - struct NamestoreClient *nc = zi->nc; - - memset (&proc, 0, sizeof(proc)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Asked to return up to %llu records at position %llu\n", - (unsigned long long) limit, - (unsigned long long) zi->seq); - proc.zi = zi; - proc.limit = limit; - proc.run_again = GNUNET_YES; - start = GNUNET_TIME_absolute_get (); - while (GNUNET_YES == proc.run_again) - { - proc.run_again = GNUNET_NO; - GNUNET_break (GNUNET_SYSERR != - nc->GSN_database->iterate_records (nc->GSN_database->cls, - (GNUNET_YES == - GNUNET_is_zero ( - &zi->zone)) - ? NULL - : &zi->zone, - zi->seq, - proc.limit, - &zone_iterate_proc, - &proc)); - } - duration = GNUNET_TIME_absolute_get_duration (start); - duration = GNUNET_TIME_relative_divide (duration, limit - proc.limit); - GNUNET_STATISTICS_set (statistics, - "NAMESTORE iteration delay (μs/record)", - duration.rel_value_us, - GNUNET_NO); - if (0 == proc.limit) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Returned %llu results, more results available\n", - (unsigned long long) limit); - zi->send_end = (0 != proc.limit); - if (0 == zi->cache_ops) - zone_iteration_done_client_continue (zi); -} - -static enum GNUNET_GenericReturnValue -check_iteration_start (void *cls, - const struct ZoneIterationStartMessage *zis_msg) -{ - uint16_t size; - size_t key_len; - - size = ntohs (zis_msg->gns_header.header.size); - key_len = ntohs (zis_msg->key_len); - - if (size < key_len + sizeof(*zis_msg)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START message - * - * @param cls the client sending the message - * @param zis_msg message from the client - */ -static void -handle_iteration_start (void *cls, - const struct ZoneIterationStartMessage *zis_msg) -{ - struct GNUNET_CRYPTO_PrivateKey zone; - struct NamestoreClient *nc = cls; - struct ZoneIteration *zi; - size_t key_len; - size_t kb_read; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ZONE_ITERATION_START message\n"); - key_len = ntohs (zis_msg->key_len); - zi = GNUNET_new (struct ZoneIteration); - if (0 < key_len) - { - if ((GNUNET_SYSERR == - GNUNET_CRYPTO_read_private_key_from_buffer (&zis_msg[1], - key_len, - &zone, - &kb_read)) || - (kb_read != key_len)) - { - GNUNET_SERVICE_client_drop (nc->client); - GNUNET_free (zi); - return; - } - zi->zone = zone; - } - zi->request_id = ntohl (zis_msg->gns_header.r_id); - zi->filter = ntohs (zis_msg->filter); - zi->offset = 0; - zi->nc = nc; - GNUNET_CONTAINER_DLL_insert (nc->op_head, nc->op_tail, zi); - run_zone_iteration_round (zi, 1); -} - - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP message - * - * @param cls the client sending the message - * @param zis_msg message from the client - */ -static void -handle_iteration_stop (void *cls, - const struct ZoneIterationStopMessage *zis_msg) -{ - struct NamestoreClient *nc = cls; - struct ZoneIteration *zi; - uint32_t rid; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ZONE_ITERATION_STOP message\n"); - rid = ntohl (zis_msg->gns_header.r_id); - for (zi = nc->op_head; NULL != zi; zi = zi->next) - if (zi->request_id == rid) - break; - if (NULL == zi) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - GNUNET_CONTAINER_DLL_remove (nc->op_head, nc->op_tail, zi); - GNUNET_free (zi); - GNUNET_SERVICE_client_continue (nc->client); -} - - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT message - * - * @param cls the client sending the message - * @param zis_msg message from the client - */ -static void -handle_iteration_next (void *cls, - const struct ZoneIterationNextMessage *zis_msg) -{ - struct NamestoreClient *nc = cls; - struct ZoneIteration *zi; - uint32_t rid; - uint64_t limit; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ZONE_ITERATION_NEXT message\n"); - GNUNET_STATISTICS_update (statistics, - "Iteration NEXT messages received", - 1, - GNUNET_NO); - rid = ntohl (zis_msg->gns_header.r_id); - limit = GNUNET_ntohll (zis_msg->limit); - for (zi = nc->op_head; NULL != zi; zi = zi->next) - if (zi->request_id == rid) - break; - if (NULL == zi) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - run_zone_iteration_round (zi, limit); -} - - -/** - * Function called when the monitor is ready for more data, and we - * should thus unblock PUT operations that were blocked on the - * monitor not being ready. - */ -static void -monitor_unblock (struct ZoneMonitor *zm) -{ - struct StoreActivity *sa = sa_head; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Unblocking zone monitor %p\n", zm); - while ((NULL != sa) && (zm->limit > zm->iteration_cnt)) - { - struct StoreActivity *sn = sa->next; - - if (sa->zm_pos == zm) - continue_store_activity (sa, GNUNET_YES); - sa = sn; - } - if (zm->limit > zm->iteration_cnt) - { - zm->sa_waiting = GNUNET_NO; - if (NULL != zm->sa_wait_warning) - { - GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); - zm->sa_wait_warning = NULL; - } - } - else if (GNUNET_YES == zm->sa_waiting) - { - zm->sa_waiting_start = GNUNET_TIME_absolute_get (); - if (NULL != zm->sa_wait_warning) - GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); - zm->sa_wait_warning = - GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, - &warn_monitor_slow, - zm); - } -} - - -/** - * Send 'sync' message to zone monitor, we're now in sync. - * - * @param zm monitor that is now in sync - */ -static void -monitor_sync (struct ZoneMonitor *zm) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *sync; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Synching zone monitor %p\n", zm); - - env = GNUNET_MQ_msg (sync, GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_SYNC); - GNUNET_MQ_send (zm->nc->mq, env); - /* mark iteration done */ - zm->in_first_iteration = GNUNET_NO; - zm->iteration_cnt = 0; - if ((zm->limit > 0) && (zm->sa_waiting)) - monitor_unblock (zm); -} - - -/** - * Obtain the next datum during the zone monitor's zone initial iteration. - * - * @param cls zone monitor that does its initial iteration - */ -static void -monitor_iteration_next (void *cls); - - -/** - * A #GNUNET_NAMESTORE_RecordIterator for monitors. - * - * @param cls a 'struct ZoneMonitor *' with information about the monitor - * @param seq sequence number of the record, MUST NOT BE ZERO - * @param zone_key zone key of the zone - * @param name name - * @param rd_count number of records in @a rd - * @param rd array of records - */ -static void -monitor_iterate_cb (void *cls, - uint64_t seq, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *name, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct ZoneMonitor *zm = cls; - - GNUNET_assert (0 != seq); - zm->seq = seq; - GNUNET_assert (NULL != name); - GNUNET_STATISTICS_update (statistics, - "Monitor notifications sent", - 1, - GNUNET_NO); - if (0 < send_lookup_response_with_filter (zm->nc, 0, zone_key, name, - rd_count, rd, zm->filter)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sent records.\n"); - zm->limit--; - zm->iteration_cnt--; - } - else - zm->run_again = GNUNET_YES; - if ((0 == zm->iteration_cnt) && (0 != zm->limit)) - { - /* We are done with the current iteration batch, AND the - client would right now accept more, so go again! */ - GNUNET_assert (NULL == zm->task); - zm->task = GNUNET_SCHEDULER_add_now (&monitor_iteration_next, zm); - } -} - -static enum GNUNET_GenericReturnValue -check_monitor_start (void *cls, - const struct ZoneMonitorStartMessage *zis_msg) -{ - uint16_t size; - size_t key_len; - - size = ntohs (zis_msg->header.size); - key_len = ntohs (zis_msg->key_len); - - if (size < key_len + sizeof(*zis_msg)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START message - * - * @param cls the client sending the message - * @param zis_msg message from the client - */ -static void -handle_monitor_start (void *cls, const struct - ZoneMonitorStartMessage *zis_msg) -{ - struct GNUNET_CRYPTO_PrivateKey zone; - struct NamestoreClient *nc = cls; - struct ZoneMonitor *zm; - size_t key_len; - size_t kb_read; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ZONE_MONITOR_START message\n"); - zm = GNUNET_new (struct ZoneMonitor); - zm->nc = nc; - key_len = ntohs (zis_msg->key_len); - if (0 < key_len) - { - if ((GNUNET_SYSERR == - GNUNET_CRYPTO_read_private_key_from_buffer (&zis_msg[1], - key_len, - &zone, - &kb_read)) || - (kb_read != key_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error reading private key\n"); - GNUNET_SERVICE_client_drop (nc->client); - GNUNET_free (zm); - return; - } - zm->zone = zone; - } - zm->limit = 1; - zm->filter = ntohs (zis_msg->filter); - zm->in_first_iteration = (GNUNET_YES == ntohl (zis_msg->iterate_first)); - GNUNET_CONTAINER_DLL_insert (monitor_head, monitor_tail, zm); - GNUNET_SERVICE_client_mark_monitor (nc->client); - GNUNET_SERVICE_client_continue (nc->client); - GNUNET_notification_context_add (monitor_nc, nc->mq); - if (zm->in_first_iteration) - zm->task = GNUNET_SCHEDULER_add_now (&monitor_iteration_next, zm); - else - monitor_sync (zm); -} - - -/** - * Obtain the next datum during the zone monitor's zone initial iteration. - * - * @param cls zone monitor that does its initial iteration - */ -static void -monitor_iteration_next (void *cls) -{ - struct ZoneMonitor *zm = cls; - struct NamestoreClient *nc = zm->nc; - int ret; - - zm->task = NULL; - GNUNET_assert (0 == zm->iteration_cnt); - if (zm->limit > 16) - zm->iteration_cnt = zm->limit / 2; /* leave half for monitor events */ - else - zm->iteration_cnt = zm->limit; /* use it all */ - zm->run_again = GNUNET_YES; - while (GNUNET_YES == zm->run_again) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Running iteration\n"); - zm->run_again = GNUNET_NO; - ret = nc->GSN_database->iterate_records (nc->GSN_database->cls, - (GNUNET_YES == GNUNET_is_zero ( - &zm->zone)) ? NULL : &zm->zone, - zm->seq, - zm->iteration_cnt, - &monitor_iterate_cb, - zm); - } - if (GNUNET_SYSERR == ret) - { - GNUNET_SERVICE_client_drop (zm->nc->client); - return; - } - if (GNUNET_NO == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Zone empty... syncing\n"); - /* empty zone */ - monitor_sync (zm); - return; - } -} - -/** - * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT message - * - * @param cls the client sending the message - * @param nm message from the client - */ -static void -handle_monitor_next (void *cls, const struct ZoneMonitorNextMessage *nm) -{ - struct NamestoreClient *nc = cls; - struct ZoneMonitor *zm; - uint64_t inc; - - inc = GNUNET_ntohll (nm->limit); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ZONE_MONITOR_NEXT message with limit %llu\n", - (unsigned long long) inc); - for (zm = monitor_head; NULL != zm; zm = zm->next) - if (zm->nc == nc) - break; - if (NULL == zm) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - GNUNET_SERVICE_client_continue (nc->client); - if (zm->limit + inc < zm->limit) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (nc->client); - return; - } - zm->limit += inc; - if ((zm->in_first_iteration) && (zm->limit == inc)) - { - /* We are still iterating, and the previous iteration must - have stopped due to the client's limit, so continue it! */ - GNUNET_assert (NULL == zm->task); - zm->task = GNUNET_SCHEDULER_add_now (&monitor_iteration_next, zm); - } - GNUNET_assert (zm->iteration_cnt <= zm->limit); - if ((zm->limit > zm->iteration_cnt) && (zm->sa_waiting)) - { - monitor_unblock (zm); - } - else if (GNUNET_YES == zm->sa_waiting) - { - if (NULL != zm->sa_wait_warning) - GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); - zm->sa_waiting_start = GNUNET_TIME_absolute_get (); - zm->sa_wait_warning = - GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, - &warn_monitor_slow, - zm); - } -} - -/** - * Process namestore requests. - * - * @param cls closure - * @param cfg configuration to use - * @param service the initialized service - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_SERVICE_Handle *service) -{ - char *database; - (void) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting namestore service\n"); - return_orphaned = GNUNET_CONFIGURATION_get_value_yesno (cfg, - "namestore", - "RETURN_ORPHANED"); - GSN_cfg = cfg; - monitor_nc = GNUNET_notification_context_create (1); - statistics = GNUNET_STATISTICS_create ("namestore", cfg); - /* Loading database plugin */ - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "namestore", - "database", - &database)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n"); - GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); - return; - } - GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_namestore_%s", database); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading %s\n", db_lib_name); - GSN_database = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); - GNUNET_free (database); - if (NULL == GSN_database) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not load database backend `%s'\n", - db_lib_name); - GNUNET_free (db_lib_name); - GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); - return; - } - GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL); -} - - -/** - * Define "main" method using service macro. - */ -GNUNET_SERVICE_MAIN ( - "namestore", - GNUNET_SERVICE_OPTION_NONE, - &run, - &client_connect_cb, - &client_disconnect_cb, - NULL, - GNUNET_MQ_hd_fixed_size (tx_control, - GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL, - struct TxControlMessage, - NULL), - GNUNET_MQ_hd_var_size (record_store, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE, - struct RecordStoreMessage, - NULL), - GNUNET_MQ_hd_var_size (record_lookup, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP, - struct LabelLookupMessage, - NULL), - GNUNET_MQ_hd_var_size (zone_to_name, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME, - struct ZoneToNameMessage, - NULL), - GNUNET_MQ_hd_var_size (iteration_start, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START, - struct ZoneIterationStartMessage, - NULL), - GNUNET_MQ_hd_fixed_size (iteration_next, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT, - struct ZoneIterationNextMessage, - NULL), - GNUNET_MQ_hd_fixed_size (iteration_stop, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP, - struct ZoneIterationStopMessage, - NULL), - GNUNET_MQ_hd_var_size (monitor_start, - GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START, - struct ZoneMonitorStartMessage, - NULL), - GNUNET_MQ_hd_fixed_size (monitor_next, - GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT, - struct ZoneMonitorNextMessage, - NULL), - GNUNET_MQ_handler_end ()); - - -/* end of gnunet-service-namestore.c */ diff --git a/src/namestore/gnunet-zoneimport.c b/src/namestore/gnunet-zoneimport.c deleted file mode 100644 index c7e0cf65f..000000000 --- a/src/namestore/gnunet-zoneimport.c +++ /dev/null @@ -1,1872 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file src/namestore/gnunet-zoneimport.c - * @brief import a DNS zone for publication in GNS, incremental - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include -#include -#include - - -/** - * Maximum number of queries pending at the same time. - */ -#define THRESH 100 - -/** - * TIME_THRESH is in usecs. How quickly do we submit fresh queries. - * Used as an additional throttle. - */ -#define TIME_THRESH 10 - -/** - * How often do we retry a query before giving up for good? - */ -#define MAX_RETRIES 5 - -/** - * How many DNS requests do we at most issue in rapid series? - */ -#define MAX_SERIES 10 - -/** - * How long do we wait at least between series of requests? - */ -#define SERIES_DELAY \ - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MICROSECONDS, 10) - -/** - * How long do DNS records have to last at least after being imported? - */ -static struct GNUNET_TIME_Relative minimum_expiration_time; - -/** - * How many requests do we request from NAMESTORE in one batch - * during our initial iteration? - */ -#define NS_BATCH_SIZE 1024 - -/** - * Some zones may include authoritative records for other - * zones, such as foo.com.uk or bar.com.fr. As for GNS - * each dot represents a zone cut, we then need to create a - * zone on-the-fly to capture those records properly. - */ -struct Zone -{ - /** - * Kept in a DLL. - */ - struct Zone *next; - - /** - * Kept in a DLL. - */ - struct Zone *prev; - - /** - * Domain of the zone (i.e. "fr" or "com.fr") - */ - char *domain; - - /** - * Private key of the zone. - */ - struct GNUNET_CRYPTO_PrivateKey key; -}; - - -/** - * Record for the request to be stored by GNS. - */ -struct Record -{ - /** - * Kept in a DLL. - */ - struct Record *next; - - /** - * Kept in a DLL. - */ - struct Record *prev; - - /** - * GNS record. - */ - struct GNUNET_GNSRECORD_Data grd; -}; - - -/** - * Request we should make. We keep this struct in memory per request, - * thus optimizing it is crucial for the overall memory consumption of - * the zone importer. - */ -struct Request -{ - /** - * Requests are kept in a heap while waiting to be resolved. - */ - struct GNUNET_CONTAINER_HeapNode *hn; - - /** - * Active requests are kept in a DLL. - */ - struct Request *next; - - /** - * Active requests are kept in a DLL. - */ - struct Request *prev; - - /** - * Head of records that should be published in GNS for - * this hostname. - */ - struct Record *rec_head; - - /** - * Tail of records that should be published in GNS for - * this hostname. - */ - struct Record *rec_tail; - - /** - * Socket used to make the request, NULL if not active. - */ - struct GNUNET_DNSSTUB_RequestSocket *rs; - - /** - * Hostname we are resolving, allocated at the end of - * this struct (optimizing memory consumption by reducing - * total number of allocations). - */ - char *hostname; - - /** - * Namestore operation pending for this record. - */ - struct GNUNET_NAMESTORE_QueueEntry *qe; - - /** - * Zone responsible for this request. - */ - const struct Zone *zone; - - /** - * At what time does the (earliest) of the returned records - * for this name expire? At this point, we need to re-fetch - * the record. - */ - struct GNUNET_TIME_Absolute expires; - - /** - * While we are fetching the record, the value is set to the - * starting time of the DNS operation. While doing a - * NAMESTORE store, again set to the start time of the - * NAMESTORE operation. - */ - struct GNUNET_TIME_Absolute op_start_time; - - /** - * How often did we issue this query? (And failed, reset - * to zero once we were successful.) - */ - unsigned int issue_num; - - /** - * random 16-bit DNS query identifier. - */ - uint16_t id; -}; - - -/** - * Command-line argument specifying desired size of the hash map with - * all of our pending names. Usually, we use an automatically growing - * map, but this is only OK up to about a million entries. Above that - * number, the user must explicitly specify the size at startup. - */ -static unsigned int map_size = 1024; - -/** - * Handle to the identity service. - */ -static struct GNUNET_IDENTITY_Handle *id; - -/** - * Namestore handle. - */ -static struct GNUNET_NAMESTORE_Handle *ns; - -/** - * Handle to the statistics service. - */ -static struct GNUNET_STATISTICS_Handle *stats; - -/** - * Context for DNS resolution. - */ -static struct GNUNET_DNSSTUB_Context *ctx; - -/** - * The number of DNS queries that are outstanding - */ -static unsigned int pending; - -/** - * The number of NAMESTORE record store operations that are outstanding - */ -static unsigned int pending_rs; - -/** - * Number of lookups we performed overall. - */ -static unsigned int lookups; - -/** - * Number of records we had cached. - */ -static unsigned int cached; - -/** - * How many hostnames did we reject (malformed). - */ -static unsigned int rejects; - -/** - * Number of lookups that failed. - */ -static unsigned int failures; - -/** - * Number of records we found. - */ -static unsigned int records; - -/** - * Number of record sets given to namestore. - */ -static unsigned int record_sets; - -/** - * Heap of all requests to perform, sorted by - * the time we should next do the request (i.e. by expires). - */ -static struct GNUNET_CONTAINER_Heap *req_heap; - -/** - * Active requests are kept in a DLL. - */ -static struct Request *req_head; - -/** - * Active requests are kept in a DLL. - */ -static struct Request *req_tail; - -/** - * Main task. - */ -static struct GNUNET_SCHEDULER_Task *t; - -/** - * Hash map of requests for which we may still get a response from - * the namestore. Set to NULL once the initial namestore iteration - * is done. - */ -static struct GNUNET_CONTAINER_MultiHashMap *ns_pending; - -/** - * Current zone iteration handle. - */ -static struct GNUNET_NAMESTORE_ZoneIterator *zone_it; - -/** - * Head of list of zones we are managing. - */ -static struct Zone *zone_head; - -/** - * Tail of list of zones we are managing. - */ -static struct Zone *zone_tail; - -/** - * After how many more results must #ns_lookup_result_cb() ask - * the namestore for more? - */ -static uint64_t ns_iterator_trigger_next; - -/** - * Number of DNS requests counted in latency total. - */ -static uint64_t total_dns_latency_cnt; - -/** - * Sum of DNS latencies observed. - */ -static struct GNUNET_TIME_Relative total_dns_latency; - -/** - * Number of records processed (DNS lookup, no NAMESTORE) in total. - */ -static uint64_t total_reg_proc_dns; - -/** - * Number of records processed (DNS lookup, with NAMESTORE) in total. - */ -static uint64_t total_reg_proc_dns_ns; - -/** - * Start time of the regular processing. - */ -static struct GNUNET_TIME_Absolute start_time_reg_proc; - -/** - * Last time we worked before going idle. - */ -static struct GNUNET_TIME_Absolute sleep_time_reg_proc; - -/** - * Time we slept just waiting for work. - */ -static struct GNUNET_TIME_Relative idle_time; - - -/** - * Callback for #for_all_records - * - * @param cls closure - * @param rec a DNS record - */ -typedef void (*RecordProcessor) (void *cls, - const struct GNUNET_DNSPARSER_Record *rec); - - -/** - * Call @a rp for each record in @a p, regardless of - * what response section it is in. - * - * @param p packet from DNS - * @param rp function to call - * @param rp_cls closure for @a rp - */ -static void -for_all_records (const struct GNUNET_DNSPARSER_Packet *p, - RecordProcessor rp, - void *rp_cls) -{ - for (unsigned int i = 0; i < p->num_answers; i++) - { - struct GNUNET_DNSPARSER_Record *rs = &p->answers[i]; - - rp (rp_cls, rs); - } - for (unsigned int i = 0; i < p->num_authority_records; i++) - { - struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i]; - - rp (rp_cls, rs); - } - for (unsigned int i = 0; i < p->num_additional_records; i++) - { - struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i]; - - rp (rp_cls, rs); - } -} - - -/** - * Return just the label of the hostname in @a req. - * - * @param req request to process hostname of - * @return statically allocated pointer to the label, - * overwritten upon the next request! - */ -static const char * -get_label (struct Request *req) -{ - static char label[64]; - const char *dot; - - dot = strchr (req->hostname, (unsigned char) '.'); - if (NULL == dot) - { - GNUNET_break (0); - return NULL; - } - if (((size_t) (dot - req->hostname)) >= sizeof(label)) - { - GNUNET_break (0); - return NULL; - } - GNUNET_memcpy (label, req->hostname, dot - req->hostname); - label[dot - req->hostname] = '\0'; - return label; -} - - -/** - * Build DNS query for @a hostname. - * - * @param hostname host to build query for - * @param[out] raw_size number of bytes in the query - * @return NULL on error, otherwise pointer to statically (!) - * allocated query buffer - */ -static void * -build_dns_query (struct Request *req, size_t *raw_size) -{ - static char raw[512]; - char *rawp; - struct GNUNET_DNSPARSER_Packet p; - struct GNUNET_DNSPARSER_Query q; - int ret; - - q.name = (char *) req->hostname; - q.type = GNUNET_DNSPARSER_TYPE_NS; - q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET; - - memset (&p, 0, sizeof(p)); - p.num_queries = 1; - p.queries = &q; - p.id = req->id; - ret = GNUNET_DNSPARSER_pack (&p, UINT16_MAX, &rawp, raw_size); - if (GNUNET_OK != ret) - { - if (GNUNET_NO == ret) - GNUNET_free (rawp); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to pack query for hostname `%s'\n", - req->hostname); - rejects++; - return NULL; - } - if (*raw_size > sizeof(raw)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to pack query for hostname `%s'\n", - req->hostname); - rejects++; - GNUNET_break (0); - GNUNET_free (rawp); - return NULL; - } - GNUNET_memcpy (raw, rawp, *raw_size); - GNUNET_free (rawp); - return raw; -} - - -/** - * Free records associated with @a req. - * - * @param req request to free records of - */ -static void -free_records (struct Request *req) -{ - struct Record *rec; - - /* Free records */ - while (NULL != (rec = req->rec_head)) - { - GNUNET_CONTAINER_DLL_remove (req->rec_head, req->rec_tail, rec); - GNUNET_free (rec); - } -} - - -/** - * Free @a req and data structures reachable from it. - * - * @param req request to free - */ -static void -free_request (struct Request *req) -{ - free_records (req); - GNUNET_free (req); -} - - -/** - * Process as many requests as possible from the queue. - * - * @param cls NULL - */ -static void -process_queue (void *cls); - - -/** - * Insert @a req into DLL sorted by next fetch time. - * - * @param req request to insert into #req_heap - */ -static void -insert_sorted (struct Request *req) -{ - req->hn = - GNUNET_CONTAINER_heap_insert (req_heap, req, req->expires.abs_value_us); - if (req == GNUNET_CONTAINER_heap_peek (req_heap)) - { - if (NULL != t) - GNUNET_SCHEDULER_cancel (t); - sleep_time_reg_proc = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL); - } -} - - -/** - * Add record to the GNS record set for @a req. - * - * @param req the request to expand GNS record set for - * @param type type to use - * @param expiration_time when should @a rec expire - * @param data raw data to store - * @param data_len number of bytes in @a data - */ -static void -add_record (struct Request *req, - uint32_t type, - struct GNUNET_TIME_Absolute expiration_time, - const void *data, - size_t data_len) -{ - struct Record *rec; - - rec = GNUNET_malloc (sizeof(struct Record) + data_len); - rec->grd.data = &rec[1]; - rec->grd.expiration_time = expiration_time.abs_value_us; - rec->grd.data_size = data_len; - rec->grd.record_type = type; - rec->grd.flags = GNUNET_GNSRECORD_RF_NONE; - GNUNET_memcpy (&rec[1], data, data_len); - GNUNET_CONTAINER_DLL_insert (req->rec_head, req->rec_tail, rec); -} - - -/** - * Closure for #check_for_glue. - */ -struct GlueClosure -{ - /** - * Overall request we are processing. - */ - struct Request *req; - - /** - * NS name we are looking for glue for. - */ - const char *ns; - - /** - * Set to #GNUNET_YES if glue was found. - */ - int found; -}; - - -/** - * Try to find glue records for a given NS record. - * - * @param cls a `struct GlueClosure *` - * @param rec record that may contain glue information - */ -static void -check_for_glue (void *cls, const struct GNUNET_DNSPARSER_Record *rec) -{ - struct GlueClosure *gc = cls; - char dst[65536]; - size_t dst_len; - size_t off; - char ip[INET6_ADDRSTRLEN + 1]; - socklen_t ip_size = (socklen_t) sizeof(ip); - struct GNUNET_TIME_Absolute expiration_time; - struct GNUNET_TIME_Relative left; - - if (0 != strcasecmp (rec->name, gc->ns)) - return; - expiration_time = rec->expiration_time; - left = GNUNET_TIME_absolute_get_remaining (expiration_time); - if (0 == left.rel_value_us) - return; /* ignore expired glue records */ - /* if expiration window is too short, bump it to configured minimum */ - if (left.rel_value_us < minimum_expiration_time.rel_value_us) - expiration_time = - GNUNET_TIME_relative_to_absolute (minimum_expiration_time); - dst_len = sizeof(dst); - off = 0; - switch (rec->type) - { - case GNUNET_DNSPARSER_TYPE_A: - if (sizeof(struct in_addr) != rec->data.raw.data_len) - { - GNUNET_break (0); - return; - } - if (NULL == inet_ntop (AF_INET, rec->data.raw.data, ip, ip_size)) - { - GNUNET_break (0); - return; - } - if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - gc->req->hostname)) && - (GNUNET_OK == - GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip))) - { - add_record (gc->req, - GNUNET_GNSRECORD_TYPE_GNS2DNS, - expiration_time, - dst, - off); - gc->found = GNUNET_YES; - } - break; - - case GNUNET_DNSPARSER_TYPE_AAAA: - if (sizeof(struct in6_addr) != rec->data.raw.data_len) - { - GNUNET_break (0); - return; - } - if (NULL == inet_ntop (AF_INET6, rec->data.raw.data, ip, ip_size)) - { - GNUNET_break (0); - return; - } - if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - gc->req->hostname)) && - (GNUNET_OK == - GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &off, ip))) - { - add_record (gc->req, - GNUNET_GNSRECORD_TYPE_GNS2DNS, - expiration_time, - dst, - off); - gc->found = GNUNET_YES; - } - break; - - case GNUNET_DNSPARSER_TYPE_CNAME: - if ((GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - gc->req->hostname)) && - (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - rec->data.hostname))) - { - add_record (gc->req, - GNUNET_GNSRECORD_TYPE_GNS2DNS, - expiration_time, - dst, - off); - gc->found = GNUNET_YES; - } - break; - - default: - /* useless, do nothing */ - break; - } -} - - -/** - * Closure for #process_record(). - */ -struct ProcessRecordContext -{ - /** - * Answer we got back and are currently parsing, or NULL - * if not active. - */ - struct GNUNET_DNSPARSER_Packet *p; - - /** - * Request we are processing. - */ - struct Request *req; -}; - - -/** - * We received @a rec for @a req. Remember the answer. - * - * @param cls a `struct ProcessRecordContext` - * @param rec response - */ -static void -process_record (void *cls, const struct GNUNET_DNSPARSER_Record *rec) -{ - struct ProcessRecordContext *prc = cls; - struct Request *req = prc->req; - char dst[65536]; - size_t dst_len; - size_t off; - struct GNUNET_TIME_Absolute expiration_time; - struct GNUNET_TIME_Relative left; - - dst_len = sizeof(dst); - off = 0; - records++; - if (0 != strcasecmp (rec->name, req->hostname)) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_DEBUG, - "DNS returned record from zone `%s' of type %u while resolving `%s'\n", - rec->name, - (unsigned int) rec->type, - req->hostname); - return; /* does not match hostname, might be glue, but - not useful for this pass! */ - } - expiration_time = rec->expiration_time; - left = GNUNET_TIME_absolute_get_remaining (expiration_time); - if (0 == left.rel_value_us) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DNS returned expired record for `%s'\n", - req->hostname); - GNUNET_STATISTICS_update (stats, - "# expired records obtained from DNS", - 1, - GNUNET_NO); - return; /* record expired */ - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DNS returned record that expires at %s for `%s'\n", - GNUNET_STRINGS_absolute_time_to_string (expiration_time), - req->hostname); - /* if expiration window is too short, bump it to configured minimum */ - if (left.rel_value_us < minimum_expiration_time.rel_value_us) - expiration_time = - GNUNET_TIME_relative_to_absolute (minimum_expiration_time); - switch (rec->type) - { - case GNUNET_DNSPARSER_TYPE_NS: { - struct GlueClosure gc; - - /* check for glue */ - gc.req = req; - gc.ns = rec->data.hostname; - gc.found = GNUNET_NO; - for_all_records (prc->p, &check_for_glue, &gc); - if ((GNUNET_NO == gc.found) && - (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - req->hostname)) && - (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - rec->data.hostname))) - { - /* FIXME: actually check if this is out-of-bailiwick, - and if not request explicit resolution... */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converted OOB (`%s') NS record for `%s'\n", - rec->data.hostname, - rec->name); - add_record (req, - GNUNET_GNSRECORD_TYPE_GNS2DNS, - expiration_time, - dst, - off); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converted NS record for `%s' using glue\n", - rec->name); - } - break; - } - - case GNUNET_DNSPARSER_TYPE_CNAME: - if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - rec->data.hostname)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converting CNAME (`%s') record for `%s'\n", - rec->data.hostname, - rec->name); - add_record (req, rec->type, expiration_time, dst, off); - } - break; - - case GNUNET_DNSPARSER_TYPE_DNAME: - /* No support for DNAME in GNS yet! FIXME: support later! */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "FIXME: not supported: %s DNAME %s\n", - rec->name, - rec->data.hostname); - break; - - case GNUNET_DNSPARSER_TYPE_MX: - if (GNUNET_OK == - GNUNET_DNSPARSER_builder_add_mx (dst, dst_len, &off, rec->data.mx)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converting MX (`%s') record for `%s'\n", - rec->data.mx->mxhost, - rec->name); - add_record (req, rec->type, expiration_time, dst, off); - } - break; - - case GNUNET_DNSPARSER_TYPE_SOA: - if (GNUNET_OK == - GNUNET_DNSPARSER_builder_add_soa (dst, dst_len, &off, rec->data.soa)) - { - /* NOTE: GNS does not really use SOAs */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converting SOA record for `%s'\n", - rec->name); - add_record (req, rec->type, expiration_time, dst, off); - } - break; - - case GNUNET_DNSPARSER_TYPE_SRV: - if (GNUNET_OK == - GNUNET_DNSPARSER_builder_add_srv (dst, dst_len, &off, rec->data.srv)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converting SRV record for `%s'\n", - rec->name); - add_record (req, rec->type, expiration_time, dst, off); - } - break; - - case GNUNET_DNSPARSER_TYPE_PTR: - if (GNUNET_OK == GNUNET_DNSPARSER_builder_add_name (dst, - dst_len, - &off, - rec->data.hostname)) - { - /* !?: what does a PTR record do in a regular TLD??? */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converting PTR record for `%s' (weird)\n", - rec->name); - add_record (req, rec->type, expiration_time, dst, off); - } - break; - - case GNUNET_DNSPARSER_TYPE_CERT: - if (GNUNET_OK == - GNUNET_DNSPARSER_builder_add_cert (dst, dst_len, &off, rec->data.cert)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converting CERT record for `%s'\n", - rec->name); - add_record (req, rec->type, expiration_time, dst, off); - } - break; - - /* Rest is 'raw' encoded and just needs to be copied IF - the hostname matches the requested name; otherwise we - simply cannot use it. */ - case GNUNET_DNSPARSER_TYPE_A: - case GNUNET_DNSPARSER_TYPE_AAAA: - case GNUNET_DNSPARSER_TYPE_TXT: - default: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Converting record of type %u for `%s'\n", - (unsigned int) rec->type, - rec->name); - add_record (req, - rec->type, - expiration_time, - rec->data.raw.data, - rec->data.raw.data_len); - break; - } -} - - -static void -store_completed_cb (void *cls, enum GNUNET_ErrorCode ec) -{ - static struct GNUNET_TIME_Absolute last; - struct Request *req = cls; - - req->qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store zone data for `%s': %s\n", - req->hostname, - GNUNET_ErrorCode_get_hint (ec)); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Stored records under `%s' (%d)\n", - req->hostname, - ec); - } - total_reg_proc_dns_ns++; /* finished regular processing */ - pending_rs--; - free_records (req); - /* compute NAMESTORE statistics */ - { - static uint64_t total_ns_latency_cnt; - static struct GNUNET_TIME_Relative total_ns_latency; - struct GNUNET_TIME_Relative ns_latency; - - ns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time); - total_ns_latency = GNUNET_TIME_relative_add (total_ns_latency, ns_latency); - if (0 == total_ns_latency_cnt) - last = GNUNET_TIME_absolute_get (); - total_ns_latency_cnt++; - if (0 == (total_ns_latency_cnt % 1000)) - { - struct GNUNET_TIME_Relative delta; - - delta = GNUNET_TIME_absolute_get_duration (last); - last = GNUNET_TIME_absolute_get (); - fprintf (stderr, - "Processed 1000 records in %s\n", - GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES)); - GNUNET_STATISTICS_set (stats, - "# average NAMESTORE PUT latency (μs)", - total_ns_latency.rel_value_us - / total_ns_latency_cnt, - GNUNET_NO); - } - } - /* compute and publish overall velocity */ - if (0 == (total_reg_proc_dns_ns % 100)) - { - struct GNUNET_TIME_Relative runtime; - - runtime = GNUNET_TIME_absolute_get_duration (start_time_reg_proc); - runtime = GNUNET_TIME_relative_subtract (runtime, idle_time); - runtime = - GNUNET_TIME_relative_divide (runtime, - total_reg_proc_dns + total_reg_proc_dns_ns); - GNUNET_STATISTICS_set (stats, - "# Regular processing completed without NAMESTORE", - total_reg_proc_dns, - GNUNET_NO); - GNUNET_STATISTICS_set (stats, - "# Regular processing completed with NAMESTORE PUT", - total_reg_proc_dns_ns, - GNUNET_NO); - GNUNET_STATISTICS_set (stats, - "# average request processing latency (μs)", - runtime.rel_value_us, - GNUNET_NO); - GNUNET_STATISTICS_set (stats, - "# total time spent idle (μs)", - idle_time.rel_value_us, - GNUNET_NO); - } - - if (NULL == t) - { - sleep_time_reg_proc = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_now (&process_queue, NULL); - } -} - - -/** - * Function called with the result of a DNS resolution. - * - * @param cls closure with the `struct Request` - * @param dns dns response, never NULL - * @param dns_len number of bytes in @a dns - */ -static void -process_result (void *cls, - const struct GNUNET_TUN_DnsHeader *dns, - size_t dns_len) -{ - struct Request *req = cls; - struct Record *rec; - struct GNUNET_DNSPARSER_Packet *p; - unsigned int rd_count; - - GNUNET_assert (NULL == req->hn); - if (NULL == dns) - { - /* stub gave up */ - GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req); - pending--; - if (NULL == t) - { - sleep_time_reg_proc = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_now (&process_queue, NULL); - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Stub gave up on DNS reply for `%s'\n", - req->hostname); - GNUNET_STATISTICS_update (stats, "# DNS lookups timed out", 1, GNUNET_NO); - if (req->issue_num > MAX_RETRIES) - { - failures++; - free_request (req); - GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO); - return; - } - total_reg_proc_dns++; - req->rs = NULL; - insert_sorted (req); - return; - } - if (req->id != dns->id) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "DNS ID did not match request, ignoring reply\n"); - GNUNET_STATISTICS_update (stats, "# DNS ID mismatches", 1, GNUNET_NO); - return; - } - GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req); - GNUNET_DNSSTUB_resolve_cancel (req->rs); - req->rs = NULL; - pending--; - p = GNUNET_DNSPARSER_parse ((const char *) dns, dns_len); - if (NULL == p) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse DNS reply for `%s'\n", - req->hostname); - GNUNET_STATISTICS_update (stats, "# DNS parser errors", 1, GNUNET_NO); - if (NULL == t) - { - sleep_time_reg_proc = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_now (&process_queue, NULL); - } - if (req->issue_num > MAX_RETRIES) - { - failures++; - free_request (req); - GNUNET_STATISTICS_update (stats, "# requests given up on", 1, GNUNET_NO); - return; - } - insert_sorted (req); - return; - } - /* import new records */ - req->issue_num = 0; /* success, reset counter! */ - { - struct ProcessRecordContext prc = { .req = req, .p = p }; - - for_all_records (p, &process_record, &prc); - } - GNUNET_DNSPARSER_free_packet (p); - /* count records found, determine minimum expiration time */ - req->expires = GNUNET_TIME_UNIT_FOREVER_ABS; - { - struct GNUNET_TIME_Relative dns_latency; - - dns_latency = GNUNET_TIME_absolute_get_duration (req->op_start_time); - total_dns_latency = - GNUNET_TIME_relative_add (total_dns_latency, dns_latency); - total_dns_latency_cnt++; - if (0 == (total_dns_latency_cnt % 1000)) - { - GNUNET_STATISTICS_set (stats, - "# average DNS lookup latency (μs)", - total_dns_latency.rel_value_us - / total_dns_latency_cnt, - GNUNET_NO); - } - } - rd_count = 0; - for (rec = req->rec_head; NULL != rec; rec = rec->next) - { - struct GNUNET_TIME_Absolute at; - - at.abs_value_us = rec->grd.expiration_time; - req->expires = GNUNET_TIME_absolute_min (req->expires, at); - rd_count++; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Obtained %u records for `%s'\n", - rd_count, - req->hostname); - /* Instead of going for SOA, simplified for now to look each - day in case we got an empty response */ - if (0 == rd_count) - { - req->expires = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS); - GNUNET_STATISTICS_update (stats, - "# empty DNS replies (usually NXDOMAIN)", - 1, - GNUNET_NO); - } - else - { - record_sets++; - } - /* convert records to namestore import format */ - { - struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; - unsigned int off = 0; - - /* convert linked list into array */ - for (rec = req->rec_head; NULL != rec; rec = rec->next) - rd[off++] = rec->grd; - pending_rs++; - req->op_start_time = GNUNET_TIME_absolute_get (); - req->qe = GNUNET_NAMESTORE_records_store (ns, - &req->zone->key, - get_label (req), - rd_count, - rd, - &store_completed_cb, - req); - GNUNET_assert (NULL != req->qe); - } - insert_sorted (req); -} - - -/** - * Process as many requests as possible from the queue. - * - * @param cls NULL - */ -static void -process_queue (void *cls) -{ - struct Request *req; - unsigned int series; - void *raw; - size_t raw_size; - struct GNUNET_TIME_Relative delay; - - (void) cls; - delay = GNUNET_TIME_absolute_get_duration (sleep_time_reg_proc); - idle_time = GNUNET_TIME_relative_add (idle_time, delay); - series = 0; - t = NULL; - while (pending + pending_rs < THRESH) - { - req = GNUNET_CONTAINER_heap_peek (req_heap); - if (NULL == req) - break; - if (NULL != req->qe) - return; /* namestore op still pending */ - if (NULL != req->rs) - { - GNUNET_break (0); - return; /* already submitted */ - } - if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0) - break; - GNUNET_assert (req == GNUNET_CONTAINER_heap_remove_root (req_heap)); - req->hn = NULL; - GNUNET_CONTAINER_DLL_insert (req_head, req_tail, req); - GNUNET_assert (NULL == req->rs); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting resolution for `%s'\n", - req->hostname); - raw = build_dns_query (req, &raw_size); - if (NULL == raw) - { - GNUNET_break (0); - free_request (req); - continue; - } - req->op_start_time = GNUNET_TIME_absolute_get (); - req->rs = GNUNET_DNSSTUB_resolve (ctx, raw, raw_size, &process_result, req); - GNUNET_assert (NULL != req->rs); - req->issue_num++; - lookups++; - pending++; - series++; - if (series > MAX_SERIES) - break; - } - if (pending + pending_rs >= THRESH) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Stopped processing queue (%u+%u/%u)]\n", - pending, - pending_rs, - THRESH); - return; /* wait for replies */ - } - req = GNUNET_CONTAINER_heap_peek (req_heap); - if (NULL == req) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Stopped processing queue: empty queue\n"); - return; - } - if (GNUNET_TIME_absolute_get_remaining (req->expires).rel_value_us > 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Waiting until %s for next record (`%s') to expire\n", - GNUNET_STRINGS_absolute_time_to_string (req->expires), - req->hostname); - if (NULL != t) - GNUNET_SCHEDULER_cancel (t); - sleep_time_reg_proc = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_at (req->expires, &process_queue, NULL); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Throttling\n"); - if (NULL != t) - GNUNET_SCHEDULER_cancel (t); - sleep_time_reg_proc = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_delayed (SERIES_DELAY, &process_queue, NULL); -} - - -/** - * Iterator called during #do_shutdown() to free requests in - * the #ns_pending map. - * - * @param cls NULL - * @param key unused - * @param value the `struct Request` to free - * @return #GNUNET_OK - */ -static int -free_request_it (void *cls, const struct GNUNET_HashCode *key, void *value) -{ - struct Request *req = value; - - (void) cls; - (void) key; - free_request (req); - return GNUNET_OK; -} - - -/** - * Clean up and terminate the process. - * - * @param cls NULL - */ -static void -do_shutdown (void *cls) -{ - struct Request *req; - struct Zone *zone; - - (void) cls; - if (NULL != id) - { - GNUNET_IDENTITY_disconnect (id); - id = NULL; - } - if (NULL != t) - { - GNUNET_SCHEDULER_cancel (t); - t = NULL; - } - while (NULL != (req = req_head)) - { - GNUNET_CONTAINER_DLL_remove (req_head, req_tail, req); - if (NULL != req->qe) - GNUNET_NAMESTORE_cancel (req->qe); - free_request (req); - } - while (NULL != (req = GNUNET_CONTAINER_heap_remove_root (req_heap))) - { - req->hn = NULL; - if (NULL != req->qe) - GNUNET_NAMESTORE_cancel (req->qe); - free_request (req); - } - if (NULL != zone_it) - { - GNUNET_NAMESTORE_zone_iteration_stop (zone_it); - zone_it = NULL; - } - if (NULL != ns) - { - GNUNET_NAMESTORE_disconnect (ns); - ns = NULL; - } - if (NULL != ctx) - { - GNUNET_DNSSTUB_stop (ctx); - ctx = NULL; - } - if (NULL != req_heap) - { - GNUNET_CONTAINER_heap_destroy (req_heap); - req_heap = NULL; - } - if (NULL != ns_pending) - { - GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &free_request_it, NULL); - GNUNET_CONTAINER_multihashmap_destroy (ns_pending); - ns_pending = NULL; - } - while (NULL != (zone = zone_head)) - { - GNUNET_CONTAINER_DLL_remove (zone_head, zone_tail, zone); - GNUNET_free (zone->domain); - GNUNET_free (zone); - } - if (NULL != stats) - { - GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - stats = NULL; - } -} - - -/** - * Iterate over all of the zones we care about and see which records - * we may need to re-fetch when. - * - * @param cls NULL - */ -static void -iterate_zones (void *cls); - - -/** - * Function called if #GNUNET_NAMESTORE_records_lookup() failed. - * Just logs an error. - * - * @param cls a `struct Zone` - */ -static void -ns_lookup_error_cb (void *cls) -{ - struct Zone *zone = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to load data from namestore for zone `%s'\n", - zone->domain); - zone_it = NULL; - ns_iterator_trigger_next = 0; - iterate_zones (NULL); -} - - -/** - * Process a record that was stored in the namestore. - * - * @param cls a `struct Zone *` - * @param key private key of the zone - * @param label label of the records - * @param rd_count number of entries in @a rd array, 0 if label was deleted - * @param rd array of records with data to store - */ -static void -ns_lookup_result_cb (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *key, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct Zone *zone = cls; - struct Request *req; - struct GNUNET_HashCode hc; - char *fqdn; - - ns_iterator_trigger_next--; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Obtained NAMESTORE reply, %llu left in round\n", - (unsigned long long) ns_iterator_trigger_next); - if (0 == ns_iterator_trigger_next) - { - ns_iterator_trigger_next = NS_BATCH_SIZE; - GNUNET_STATISTICS_update (stats, - "# NAMESTORE records requested from cache", - ns_iterator_trigger_next, - GNUNET_NO); - GNUNET_NAMESTORE_zone_iterator_next (zone_it, ns_iterator_trigger_next); - } - GNUNET_asprintf (&fqdn, "%s.%s", label, zone->domain); - GNUNET_CRYPTO_hash (fqdn, strlen (fqdn) + 1, &hc); - GNUNET_free (fqdn); - req = GNUNET_CONTAINER_multihashmap_get (ns_pending, &hc); - if (NULL == req) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Ignoring record `%s' in zone `%s': not on my list!\n", - label, - zone->domain); - return; - } - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_remove (ns_pending, &hc, req)); - GNUNET_break (0 == GNUNET_memcmp (key, &req->zone->key)); - GNUNET_break (0 == strcasecmp (label, get_label (req))); - for (unsigned int i = 0; i < rd_count; i++) - { - struct GNUNET_TIME_Absolute at; - - if (0 != (rd->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) - { - struct GNUNET_TIME_Relative rel; - - rel.rel_value_us = rd->expiration_time; - at = GNUNET_TIME_relative_to_absolute (rel); - } - else - { - at.abs_value_us = rd->expiration_time; - } - add_record (req, rd->record_type, at, rd->data, rd->data_size); - } - if (0 == rd_count) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Empty record set in namestore for `%s'\n", - req->hostname); - } - else - { - unsigned int pos = 0; - - cached++; - req->expires = GNUNET_TIME_UNIT_FOREVER_ABS; - for (struct Record *rec = req->rec_head; NULL != rec; rec = rec->next) - { - struct GNUNET_TIME_Absolute at; - - at.abs_value_us = rec->grd.expiration_time; - req->expires = GNUNET_TIME_absolute_min (req->expires, at); - pos++; - } - if (0 == pos) - req->expires = GNUNET_TIME_UNIT_ZERO_ABS; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Hot-start with %u existing records for `%s'\n", - pos, - req->hostname); - } - free_records (req); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Adding `%s' to worklist to start at %s\n", - req->hostname, - GNUNET_STRINGS_absolute_time_to_string (req->expires)); - insert_sorted (req); -} - - -/** - * Add @a hostname to the list of requests to be made. - * - * @param hostname name to resolve - */ -static void -queue (const char *hostname) -{ - struct Request *req; - const char *dot; - struct Zone *zone; - size_t hlen; - struct GNUNET_HashCode hc; - - if (GNUNET_OK != GNUNET_DNSPARSER_check_name (hostname)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Refusing invalid hostname `%s'\n", - hostname); - rejects++; - return; - } - dot = strchr (hostname, (unsigned char) '.'); - if (NULL == dot) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Refusing invalid hostname `%s' (lacks '.')\n", - hostname); - rejects++; - return; - } - for (zone = zone_head; NULL != zone; zone = zone->next) - if (0 == strcmp (zone->domain, dot + 1)) - break; - if (NULL == zone) - { - rejects++; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Domain name `%s' not in ego list!\n", - dot + 1); - return; - } - - hlen = strlen (hostname) + 1; - req = GNUNET_malloc (sizeof(struct Request) + hlen); - req->zone = zone; - req->hostname = (char *) &req[1]; - GNUNET_memcpy (req->hostname, hostname, hlen); - req->id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, - UINT16_MAX); - GNUNET_CRYPTO_hash (req->hostname, hlen, &hc); - if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( - ns_pending, - &hc, - req, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Duplicate hostname `%s' ignored\n", - hostname); - GNUNET_free (req); - return; - } -} - - -/** - * We have completed the initial iteration over the namestore's database. - * This function is called on each of the remaining records in - * #move_to_queue to #queue() them, as we will simply not find existing - * records for them any longer. - * - * @param cls NULL - * @param key unused - * @param value a `struct Request` - * @return #GNUNET_OK (continue to iterate) - */ -static int -move_to_queue (void *cls, const struct GNUNET_HashCode *key, void *value) -{ - struct Request *req = value; - - (void) cls; - (void) key; - insert_sorted (req); - return GNUNET_OK; -} - - -/** - * Iterate over all of the zones we care about and see which records - * we may need to re-fetch when. - * - * @param cls NULL - */ -static void -iterate_zones (void *cls) -{ - static struct Zone *last; - - (void) cls; - if (NULL != zone_it) - { - zone_it = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finished iteration over zone `%s'!\n", - last->domain); - /* subtract left-overs from previous iteration */ - GNUNET_STATISTICS_update (stats, - "# NAMESTORE records requested from cache", - (long long) (-ns_iterator_trigger_next), - GNUNET_NO); - ns_iterator_trigger_next = 0; - } - GNUNET_assert (NULL != zone_tail); - if (zone_tail == last) - { - /* Done iterating over relevant zones in NAMESTORE, move - rest of hash map to work queue as well. */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Finished all NAMESTORE iterations!\n"); - GNUNET_STATISTICS_set (stats, - "# Domain names without cached reply", - GNUNET_CONTAINER_multihashmap_size (ns_pending), - GNUNET_NO); - GNUNET_CONTAINER_multihashmap_iterate (ns_pending, &move_to_queue, NULL); - GNUNET_CONTAINER_multihashmap_destroy (ns_pending); - ns_pending = NULL; - start_time_reg_proc = GNUNET_TIME_absolute_get (); - total_reg_proc_dns = 0; - total_reg_proc_dns_ns = 0; - return; - } - if (NULL == last) - last = zone_head; - else - last = last->next; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting iteration over zone `%s'!\n", - last->domain); - /* subtract left-overs from previous iteration */ - GNUNET_STATISTICS_update (stats, - "# NAMESTORE records requested from cache", - 1, - GNUNET_NO); - ns_iterator_trigger_next = 1; - GNUNET_STATISTICS_update (stats, "# zones iterated", 1, GNUNET_NO); - zone_it = GNUNET_NAMESTORE_zone_iteration_start (ns, - &last->key, - &ns_lookup_error_cb, - NULL, - &ns_lookup_result_cb, - last, - &iterate_zones, - NULL); -} - - -/** - * Begin processing hostnames from stdin. - * - * @param cls NULL - */ -static void -process_stdin (void *cls) -{ - static struct GNUNET_TIME_Absolute last; - static uint64_t idot; - char hn[256]; - - (void) cls; - t = NULL; - if (NULL != id) - { - GNUNET_IDENTITY_disconnect (id); - id = NULL; - } - while (NULL != fgets (hn, sizeof(hn), stdin)) - { - if (strlen (hn) > 0) - hn[strlen (hn) - 1] = '\0'; /* eat newline */ - if (0 == idot) - last = GNUNET_TIME_absolute_get (); - idot++; - if (0 == idot % 100000) - { - struct GNUNET_TIME_Relative delta; - - delta = GNUNET_TIME_absolute_get_duration (last); - last = GNUNET_TIME_absolute_get (); - fprintf (stderr, - "Read 100000 domain names in %s\n", - GNUNET_STRINGS_relative_time_to_string (delta, GNUNET_YES)); - GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO); - } - queue (hn); - } - fprintf (stderr, - "Done reading %llu domain names\n", - (unsigned long long) idot); - GNUNET_STATISTICS_set (stats, "# domain names provided", idot, GNUNET_NO); - iterate_zones (NULL); -} - - -/** - * Method called to inform about the egos of this peer. - * - * When used with #GNUNET_IDENTITY_connect, this function is - * initially called for all egos and then again whenever a - * ego's name changes or if it is deleted. At the end of - * the initial pass over all egos, the function is once called - * with 'NULL' for @a ego. That does NOT mean that the callback won't - * be invoked in the future or that there was an error. - * - * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get, this - * function is only called ONCE, and 'NULL' being passed in @a ego does - * indicate an error (for example because name is taken or no default value is - * known). If @a ego is non-NULL and if '*ctx' is set in those callbacks, the - * value WILL be passed to a subsequent call to the identity callback of - * #GNUNET_IDENTITY_connect (if that one was not NULL). - * - * When an identity is renamed, this function is called with the - * (known) @a ego but the NEW @a name. - * - * When an identity is deleted, this function is called with the - * (known) ego and "NULL" for the @a name. In this case, - * the @a ego is henceforth invalid (and the @a ctx should also be - * cleaned up). - * - * @param cls closure - * @param ego ego handle, NULL for end of list - * @param ctx context for application to store data for this ego - * (during the lifetime of this process, initially NULL) - * @param name name assigned by the user for this ego, - * NULL if the user just deleted the ego and it - * must thus no longer be used - */ -static void -identity_cb (void *cls, - struct GNUNET_IDENTITY_Ego *ego, - void **ctx, - const char *name) -{ - (void) cls; - (void) ctx; - - if (NULL == ego) - { - /* end of iteration */ - if (NULL == zone_head) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No zone found\n"); - GNUNET_SCHEDULER_shutdown (); - return; - } - /* zone_head non-null, process hostnames from stdin */ - t = GNUNET_SCHEDULER_add_now (&process_stdin, NULL); - return; - } - if (NULL != name) - { - struct Zone *zone; - - zone = GNUNET_new (struct Zone); - zone->key = *GNUNET_IDENTITY_ego_get_private_key (ego); - zone->domain = GNUNET_strdup (name); - GNUNET_CONTAINER_DLL_insert (zone_head, zone_tail, zone); - } -} - - -/** - * Process requests from the queue, then if the queue is - * not empty, try again. - * - * @param cls NULL - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param cfg configuration - */ -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - (void) cls; - (void) args; - (void) cfgfile; - stats = GNUNET_STATISTICS_create ("zoneimport", cfg); - req_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - ns_pending = GNUNET_CONTAINER_multihashmap_create (map_size, GNUNET_NO); - if (NULL == ns_pending) - { - fprintf (stderr, "Failed to allocate memory for main hash map\n"); - return; - } - ctx = GNUNET_DNSSTUB_start (256); - if (NULL == ctx) - { - fprintf (stderr, "Failed to initialize GNUnet DNS STUB\n"); - return; - } - if (NULL == args[0]) - { - fprintf (stderr, - "You must provide a list of DNS resolvers on the command line\n"); - return; - } - for (unsigned int i = 0; NULL != args[i]; i++) - { - if (GNUNET_OK != GNUNET_DNSSTUB_add_dns_ip (ctx, args[i])) - { - fprintf (stderr, "Failed to use `%s' for DNS resolver\n", args[i]); - return; - } - } - - - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - ns = GNUNET_NAMESTORE_connect (cfg); - if (NULL == ns) - { - GNUNET_SCHEDULER_shutdown (); - return; - } - id = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL); -} - - -/** - * Call with IP address of resolver to query. - * - * @param argc should be 2 - * @param argv[1] should contain IP address - * @return 0 on success - */ -int -main (int argc, char *const *argv) -{ - struct GNUNET_GETOPT_CommandLineOption options[] = - { GNUNET_GETOPT_option_uint ('s', - "size", - "MAPSIZE", - gettext_noop ( - "size to use for the main hash map"), - &map_size), - GNUNET_GETOPT_option_relative_time ( - 'm', - "minimum-expiration", - "RELATIVETIME", - gettext_noop ("minimum expiration time we assume for imported records"), - &minimum_expiration_time), - GNUNET_GETOPT_OPTION_END }; - int ret; - - if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) - return 2; - if (GNUNET_OK != (ret = GNUNET_PROGRAM_run (argc, - argv, - "gnunet-zoneimport", - "import DNS zone into namestore", - options, - &run, - NULL))) - return ret; - GNUNET_free_nz ((void *) argv); - fprintf (stderr, - "Rejected %u names, had %u cached, did %u lookups, stored %u record sets\n" - "Found %u records, %u lookups failed, %u/%u pending on shutdown\n", - rejects, - cached, - lookups, - record_sets, - records, - failures, - pending, - pending_rs); - return 0; -} - - -/* end of gnunet-zoneimport.c */ diff --git a/src/namestore/meson.build b/src/namestore/meson.build deleted file mode 100644 index fd7cfe553..000000000 --- a/src/namestore/meson.build +++ /dev/null @@ -1,139 +0,0 @@ -libgnunetnamestore_src = ['namestore_api.c', 'namestore_api_monitor.c'] -libgnunetpluginnamestore_sqlite_src = ['plugin_namestore_sqlite.c'] - -gnunetnamestore_src = ['gnunet-namestore.c'] -gnunetservicenamestore_src = ['gnunet-service-namestore.c'] - -configure_file(input : 'namestore.conf.in', - output : 'namestore.conf', - configuration : cdata, - install: true, - install_dir: pkgcfgdir) - - -if get_option('monolith') - foreach p : libgnunetnamestore_src + libgnunetpluginnamestore_sqlite_src + gnunetservicenamestore_src - gnunet_src += 'namestore/' + p - endforeach - subdir_done() -endif - -libgnunetnamestore = library('gnunetnamestore', - libgnunetnamestore_src, - soversion: '0', - version: '0.0.1', - dependencies: [libgnunetutil_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')) -libgnunetnamestore_dep = declare_dependency(link_with : libgnunetnamestore) -pkg.generate(libgnunetnamestore, url: 'https://www.gnunet.org', - description : 'Provides API for storing GNS records to a database') - -shared_module('gnunet_plugin_rest_namestore', - ['plugin_rest_namestore.c'], - dependencies: [libgnunetrest_dep, - libgnunetidentity_dep, - libgnunetgnsrecordjson_dep, - libgnunetgnsrecord_dep, - libgnunetnamestore_dep, - libgnunetjson_dep, - libgnunetutil_dep, - json_dep, - mhd_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir') / 'gnunet') - - -shared_module('gnunet_plugin_namestore_sqlite', - libgnunetpluginnamestore_sqlite_src, - dependencies: [libgnunetutil_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep, - libgnunetsq_dep, - libgnunetstatistics_dep, - sqlite_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') - -if pq_dep.found() - shared_module('gnunet_plugin_namestore_postgres', - ['plugin_namestore_postgres.c'], - dependencies: [libgnunetutil_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep, - libgnunetpq_dep, - libgnunetstatistics_dep, - pq_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet') -endif - -executable ('gnunet-namestore', - gnunetnamestore_src, - dependencies: [libgnunetnamestore_dep, - libgnunetutil_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('bindir')) -executable ('gnunet-namestore-dbtool', - ['gnunet-namestore-dbtool.c'], - dependencies: [libgnunetnamestore_dep, - libgnunetutil_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('bindir')) -executable ('gnunet-namestore-zonefile', - ['gnunet-namestore-zonefile.c'], - dependencies: [libgnunetnamestore_dep, - libgnunetutil_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('bindir')) -executable ('gnunet-zoneimport', - ['gnunet-zoneimport.c'], - dependencies: [libgnunetnamestore_dep, - libgnunetutil_dep, - libgnunetstatistics_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('bindir')) -executable ('gnunet-service-namestore', - gnunetservicenamestore_src, - dependencies: [libgnunetnamestore_dep, - libgnunetutil_dep, - libgnunetnamecache_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep, - libgnunetstatistics_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet'/'libexec') -executable ('gnunet-namestore-fcfsd', - ['gnunet-namestore-fcfsd.c'], - dependencies: [libgnunetnamestore_dep, - libgnunetutil_dep, - libgnunetnamecache_dep, - libgnunetgnsrecord_dep, - libgnunetidentity_dep, - mhd_dep, - json_dep, - libgnunetjson_dep, - libgnunetstatistics_dep], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir')/'gnunet'/'libexec') - diff --git a/src/namestore/namestore-0001.sql b/src/namestore/namestore-0001.sql deleted file mode 100644 index bdfb31976..000000000 --- a/src/namestore/namestore-0001.sql +++ /dev/null @@ -1,48 +0,0 @@ --- --- This file is part of GNUnet --- Copyright (C) 2014--2022 GNUnet e.V. --- --- GNUnet is free software; you can redistribute it and/or modify it under the --- terms of the GNU General Public License as published by the Free Software --- Foundation; either version 3, or (at your option) any later version. --- --- GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY --- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR --- A PARTICULAR PURPOSE. See the GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License along with --- GNUnet; see the file COPYING. If not, see --- - --- Everything in one big transaction -BEGIN; - --- Check patch versioning is in place. -SELECT _v.register_patch('namestore-0001', NULL, NULL); - --------------------- Schema ---------------------------- - -CREATE SCHEMA namestore; -COMMENT ON SCHEMA namestore IS 'gnunet-namestore data'; - -SET search_path TO namestore; - -CREATE TABLE ns098records ( - seq BIGSERIAL PRIMARY KEY, - zone_private_key BYTEA NOT NULL DEFAULT '', - pkey BYTEA DEFAULT '', - rvalue BYTEA NOT NULL DEFAULT '', - record_count INTEGER NOT NULL DEFAULT 0, - record_data BYTEA NOT NULL DEFAULT '', - label TEXT NOT NULL DEFAULT '', - CONSTRAINT zl UNIQUE (zone_private_key,label)); - -CREATE INDEX IF NOT EXISTS ir_pkey_reverse - ON ns098records (zone_private_key,pkey); -CREATE INDEX IF NOT EXISTS ir_pkey_iter - ON ns098records (zone_private_key,seq); -CREATE INDEX IF NOT EXISTS ir_label - ON ns098records (label); - - -COMMIT; diff --git a/src/namestore/namestore-drop.sql b/src/namestore/namestore-drop.sql deleted file mode 100644 index 231417af8..000000000 --- a/src/namestore/namestore-drop.sql +++ /dev/null @@ -1,25 +0,0 @@ --- --- This file is part of GNUnet --- Copyright (C) 2014--2022 GNUnet e.V. --- --- GNUnet is free software; you can redistribute it and/or modify it under the --- terms of the GNU General Public License as published by the Free Software --- Foundation; either version 3, or (at your option) any later version. --- --- GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY --- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR --- A PARTICULAR PURPOSE. See the GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License along with --- GNUnet; see the file COPYING. If not, see --- - --- Everything in one big transaction -BEGIN; - - -SELECT _v.unregister_patch('namestore-0001'); - -DROP SCHEMA namestore CASCADE; - -COMMIT; diff --git a/src/namestore/namestore.conf.in b/src/namestore/namestore.conf.in deleted file mode 100644 index d817f3f95..000000000 --- a/src/namestore/namestore.conf.in +++ /dev/null @@ -1,47 +0,0 @@ -[namestore] -START_ON_DEMAND = @START_ON_DEMAND@ -RUN_PER_USER = YES -UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-namestore.sock -UNIX_MATCH_UID = NO -UNIX_MATCH_GID = YES -@UNIXONLY@ PORT = 2099 -HOSTNAME = localhost -BINARY = gnunet-service-namestore -ACCEPT_FROM = 127.0.0.1; -ACCEPT_FROM6 = ::1; - -# Which database should we use? -DATABASE = sqlite - -# Should we optimize publishing record by caching the mapping -# from zone private keys to zone public keys in memory? -# (Set to NO if totally paranoid about keeping private keys -# in RAM longer than necessary.) -CACHE_KEYS = YES - - -[namestore-sqlite] -INIT_ON_CONNECT = YES -FILENAME = $GNUNET_DATA_HOME/namestore/sqlite.db - -[namestore-postgres] -# How to connect to the database -CONFIG = postgres:///gnunet -# Use asynchronous commit (SET synchronous_commit TO OFF). -ASYNC_COMMIT = NO -INIT_ON_CONNECT = YES -SQL_DIR = ${DATADIR}/sql/ - -[uri] -gns = gnunet-namestore -e 1a -u - - -[fcfsd] -# Name of the fcfs registration service binary (for ARM) -BINARY = gnunet-namestore-fcfsd -START_ON_DEMAND = NO -UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-fcfsd.sock -RELATIVE_RECORD_EXPIRATION = 7 d - -# On what port does the FCFS daemon listen for HTTP clients? -HTTPPORT = 18080 diff --git a/src/namestore/namestore.h b/src/namestore/namestore.h deleted file mode 100644 index 35d54d317..000000000 --- a/src/namestore/namestore.h +++ /dev/null @@ -1,491 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011-2013 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file namestore/namestore.h - * @brief common internal definitions for namestore service - * @author Matthias Wachs - * @author Christian Grothoff - */ -#ifndef NAMESTORE_H -#define NAMESTORE_H - -/** - * Maximum length of any name, including 0-termination. - */ -#define MAX_NAME_LEN 256 - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * Generic namestore message with op id - */ -struct GNUNET_NAMESTORE_Header -{ - /** - * header.type will be GNUNET_MESSAGE_TYPE_NAMESTORE_* - * header.size will be message size - */ - struct GNUNET_MessageHeader header; - - /** - * Request ID in NBO - */ - uint32_t r_id GNUNET_PACKED; -}; - -struct RecordSet -{ - /** - * Name length - */ - uint16_t name_len GNUNET_PACKED; - - /** - * Length of serialized record data - */ - uint16_t rd_len GNUNET_PACKED; - - /** - * Number of records contained - */ - uint16_t rd_count GNUNET_PACKED; - - /** - * Reserved for alignment. - */ - uint16_t reserved GNUNET_PACKED; - - - /* followed by: - * name with length name_len - * serialized record data with rd_count records - */ -}; - -/** - * Store a record to the namestore (as authority). - */ -struct RecordStoreMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Number of record sets - */ - uint16_t rd_set_count; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /** - * Followed by the private zone key - * Followed by rd_set_count RecordSets - */ -}; - - -/** - * Response to a record storage request. - */ -struct RecordStoreResponseMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * GNUNET_ErrorCode - */ - uint32_t ec GNUNET_PACKED; - -}; - - -/** - * Lookup a label - */ -struct LabelLookupMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Length of the name - */ - uint16_t label_len GNUNET_PACKED; - - /** - * GNUNET_YES if this lookup corresponds to an edit request - */ - uint16_t is_edit_request GNUNET_PACKED; - - /** - * The record filter - */ - uint16_t filter; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /* followed by: - * the private zone key - * name with length name_len - */ -}; - - -/** - * Lookup a label - */ -struct LabelLookupResponseMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Name length - */ - uint16_t name_len GNUNET_PACKED; - - /** - * Length of serialized record data - */ - uint16_t rd_len GNUNET_PACKED; - - /** - * Number of records contained - */ - uint16_t rd_count GNUNET_PACKED; - - /** - * Was the label found in the database?? - * #GNUNET_YES or #GNUNET_NO - */ - int16_t found GNUNET_PACKED; - - /** - * Reserved (alignment) - */ - uint16_t reserved GNUNET_PACKED; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /* followed by: - * the private zone key - * name with length name_len - * serialized record data with rd_count records - */ -}; - - -/** - * Lookup a name for a zone hash - */ -struct ZoneToNameMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /** - * Length of the public value zone key - */ - uint16_t pkey_len GNUNET_PACKED; - - /** - * Followed by - * - the private zone key to look up in - * - the public key of the target zone - */ -}; - - -/** - * Respone for zone to name lookup - */ -struct ZoneToNameResponseMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * result in NBO: #GNUNET_EC_NONE on success, - * #GNUNET_EC_NAMESTORE_NO_RESULTS if there were no - * results. - * Other error messages on error. - */ - int32_t ec GNUNET_PACKED; - - /** - * Length of the name - */ - uint16_t name_len GNUNET_PACKED; - - /** - * Length of serialized record data - */ - uint16_t rd_len GNUNET_PACKED; - - /** - * Number of records contained - */ - uint16_t rd_count GNUNET_PACKED; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /* followed by: - * the private zone key - * name with length name_len - * serialized record data with rd_count records - */ -}; - - -/** - * Record is returned from the namestore (as authority). - */ -struct RecordResultMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Expiration time if the record result (if any). - * Takes TOMBSTONEs into account. - */ - struct GNUNET_TIME_AbsoluteNBO expire; - - /** - * Name length - */ - uint16_t name_len GNUNET_PACKED; - - /** - * Length of serialized record data - */ - uint16_t rd_len GNUNET_PACKED; - - /** - * Number of records contained - */ - uint16_t rd_count GNUNET_PACKED; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /* followed by: - * the private key of the authority - * name with length name_len - * serialized record data with rd_count records - */ -}; - -/** - * Send a transaction control message. - */ -struct TxControlMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * always zero (for alignment) - */ - uint16_t reserved GNUNET_PACKED; - - /** - * The type of control message to send - */ - uint16_t control GNUNET_PACKED; - -}; - -/** - * Result of a transaction control message. - */ -struct TxControlResultMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL_RESULT - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Of type GNUNET_ErrorCode - */ - uint32_t ec GNUNET_PACKED; - -}; - - - -/** - * Start monitoring a zone. - */ -struct ZoneMonitorStartMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START - */ - struct GNUNET_MessageHeader header; - - /** - * #GNUNET_YES to first iterate over all records, - * #GNUNET_NO to only monitor changes.o - */ - uint32_t iterate_first GNUNET_PACKED; - - /** - * Record set filter control flags. - * See GNUNET_NAMESTORE_Filter enum. - */ - uint16_t filter; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /** - * Followed by the private zone key. - */ -}; - - -/** - * Ask for next result of zone iteration for the given operation - */ -struct ZoneMonitorNextMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT - */ - struct GNUNET_MessageHeader header; - - /** - * Always zero. - */ - uint32_t reserved; - - /** - * Number of records to return to the iterator in one shot - * (before #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_MONITOR_NEXT - * should be send again). In NBO. - */ - uint64_t limit; -}; - - -/** - * Start a zone iteration for the given zone - */ -struct ZoneIterationStartMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Record set filter control flags. - * See GNUNET_NAMESTORE_Filter enum. - */ - uint16_t filter; - - /** - * Length of the zone key - */ - uint16_t key_len GNUNET_PACKED; - - /** - * Followed by the private zone key (optional) - */ -}; - - -/** - * Ask for next result of zone iteration for the given operation - */ -struct ZoneIterationNextMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT - */ - struct GNUNET_NAMESTORE_Header gns_header; - - /** - * Number of records to return to the iterator in one shot - * (before #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT - * should be send again). In NBO. - */ - uint64_t limit; -}; - - -/** - * Stop zone iteration for the given operation - */ -struct ZoneIterationStopMessage -{ - /** - * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP - */ - struct GNUNET_NAMESTORE_Header gns_header; -}; - - -GNUNET_NETWORK_STRUCT_END - - -/* end of namestore.h */ -#endif diff --git a/src/namestore/namestore_api.c b/src/namestore/namestore_api.c deleted file mode 100644 index 7a4438e30..000000000 --- a/src/namestore/namestore_api.c +++ /dev/null @@ -1,1563 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2010-2013, 2016 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file namestore/namestore_api.c - * @brief API to access the NAMESTORE service - * @author Martin Schanzenbach - * @author Matthias Wachs - * @author Christian Grothoff - */ - -#include "platform.h" -#include "gnunet_error_codes.h" -#include "gnunet_util_lib.h" -#include "gnunet_constants.h" -#include "gnunet_arm_service.h" -#include "gnunet_signatures.h" -#include "gnunet_gns_service.h" -#include "gnunet_namestore_service.h" -#include "namestore.h" - - -#define LOG(kind, ...) GNUNET_log_from (kind, "namestore-api", __VA_ARGS__) - -/** - * We grant the namestore up to 1 minute of latency, if it is slower than - * that, store queries will fail. - */ -#define NAMESTORE_DELAY_TOLERANCE GNUNET_TIME_UNIT_MINUTES - -/** - * An QueueEntry used to store information for a pending - * NAMESTORE record operation - */ -struct GNUNET_NAMESTORE_QueueEntry -{ - /** - * Kept in a DLL. - */ - struct GNUNET_NAMESTORE_QueueEntry *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_NAMESTORE_QueueEntry *prev; - - /** - * Main handle to access the namestore. - */ - struct GNUNET_NAMESTORE_Handle *h; - - /** - * Continuation to call - */ - GNUNET_NAMESTORE_ContinuationWithStatus cont; - - /** - * Closure for @e cont. - */ - void *cont_cls; - - /** - * Function to call with the records we get back; or NULL. - */ - GNUNET_NAMESTORE_RecordMonitor proc; - - /** - * Function to call with the records we get back; or NULL. - */ - GNUNET_NAMESTORE_RecordSetMonitor proc2; - - /** - * Closure for @e proc. - */ - void *proc_cls; - - /** - * Function to call on errors. - */ - GNUNET_SCHEDULER_TaskCallback error_cb; - - /** - * Closure for @e error_cb. - */ - void *error_cb_cls; - - /** - * Envelope of the message to send to the service, if not yet - * sent. - */ - struct GNUNET_MQ_Envelope *env; - - /** - * Task scheduled to warn us if the namestore is way too slow. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * The operation id this zone iteration operation has - */ - uint32_t op_id; -}; - - -/** - * Handle for a zone iterator operation - */ -struct GNUNET_NAMESTORE_ZoneIterator -{ - /** - * Kept in a DLL. - */ - struct GNUNET_NAMESTORE_ZoneIterator *next; - - /** - * Kept in a DLL. - */ - struct GNUNET_NAMESTORE_ZoneIterator *prev; - - /** - * Main handle to access the namestore. - */ - struct GNUNET_NAMESTORE_Handle *h; - - /** - * Function to call on completion. - */ - GNUNET_SCHEDULER_TaskCallback finish_cb; - - /** - * Closure for @e error_cb. - */ - void *finish_cb_cls; - - /** - * The continuation to call with the results - */ - GNUNET_NAMESTORE_RecordMonitor proc; - - /** - * The continuation to call with the results - */ - GNUNET_NAMESTORE_RecordSetMonitor proc2; - - /** - * Closure for @e proc. - */ - void *proc_cls; - - /** - * Function to call on errors. - */ - GNUNET_SCHEDULER_TaskCallback error_cb; - - /** - * Closure for @e error_cb. - */ - void *error_cb_cls; - - /** - * Envelope of the message to send to the service, if not yet - * sent. - */ - struct GNUNET_MQ_Envelope *env; - - /** - * Private key of the zone. - */ - struct GNUNET_CRYPTO_PrivateKey zone; - - /** - * The operation id this zone iteration operation has - */ - uint32_t op_id; -}; - - -/** - * Connection to the NAMESTORE service. - */ -struct GNUNET_NAMESTORE_Handle -{ - /** - * Configuration to use. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Connection to the service (if available). - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Head of pending namestore queue entries - */ - struct GNUNET_NAMESTORE_QueueEntry *op_head; - - /** - * Tail of pending namestore queue entries - */ - struct GNUNET_NAMESTORE_QueueEntry *op_tail; - - /** - * Head of pending namestore zone iterator entries - */ - struct GNUNET_NAMESTORE_ZoneIterator *z_head; - - /** - * Tail of pending namestore zone iterator entries - */ - struct GNUNET_NAMESTORE_ZoneIterator *z_tail; - - /** - * Reconnect task - */ - struct GNUNET_SCHEDULER_Task *reconnect_task; - - /** - * Delay introduced before we reconnect. - */ - struct GNUNET_TIME_Relative reconnect_delay; - - /** - * Should we reconnect to service due to some serious error? - */ - int reconnect; - - /** - * The last operation id used for a NAMESTORE operation - */ - uint32_t last_op_id_used; -}; - - -/** - * Disconnect from service and then reconnect. - * - * @param h our handle - */ -static void -force_reconnect (struct GNUNET_NAMESTORE_Handle *h); - - -/** - * Find the queue entry that matches the @a rid - * - * @param h namestore handle - * @param rid id to look up - * @return NULL if @a rid was not found - */ -static struct GNUNET_NAMESTORE_QueueEntry * -find_qe (struct GNUNET_NAMESTORE_Handle *h, uint32_t rid) -{ - struct GNUNET_NAMESTORE_QueueEntry *qe; - - for (qe = h->op_head; qe != NULL; qe = qe->next) - if (qe->op_id == rid) - return qe; - return NULL; -} - - -/** - * Find the zone iteration entry that matches the @a rid - * - * @param h namestore handle - * @param rid id to look up - * @return NULL if @a rid was not found - */ -static struct GNUNET_NAMESTORE_ZoneIterator * -find_zi (struct GNUNET_NAMESTORE_Handle *h, uint32_t rid) -{ - struct GNUNET_NAMESTORE_ZoneIterator *ze; - - for (ze = h->z_head; ze != NULL; ze = ze->next) - if (ze->op_id == rid) - return ze; - return NULL; -} - - -/** - * Free @a qe. - * - * @param qe entry to free - */ -static void -free_qe (struct GNUNET_NAMESTORE_QueueEntry *qe) -{ - struct GNUNET_NAMESTORE_Handle *h = qe->h; - - GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, qe); - if (NULL != qe->env) - GNUNET_MQ_discard (qe->env); - if (NULL != qe->timeout_task) - GNUNET_SCHEDULER_cancel (qe->timeout_task); - GNUNET_free (qe); -} - - -/** - * Free @a ze. - * - * @param ze entry to free - */ -static void -free_ze (struct GNUNET_NAMESTORE_ZoneIterator *ze) -{ - struct GNUNET_NAMESTORE_Handle *h = ze->h; - - GNUNET_CONTAINER_DLL_remove (h->z_head, h->z_tail, ze); - if (NULL != ze->env) - GNUNET_MQ_discard (ze->env); - GNUNET_free (ze); -} - - -/** - * Check that @a rd_buf of length @a rd_len contains - * @a rd_count records. - * - * @param rd_len length of @a rd_buf - * @param rd_buf buffer with serialized records - * @param rd_count number of records expected - * @return #GNUNET_OK if @a rd_buf is well-formed - */ -static int -check_rd (size_t rd_len, const void *rd_buf, unsigned int rd_count) -{ - struct GNUNET_GNSRECORD_Data rd[rd_count]; - - if (GNUNET_OK != - GNUNET_GNSRECORD_records_deserialize (rd_len, rd_buf, rd_count, rd)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - -/** - * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE - * - * @param cls - * @param msg the message we received - */ -static void -handle_record_store_response (void *cls, - const struct RecordStoreResponseMessage *msg) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - struct GNUNET_NAMESTORE_QueueEntry *qe; - enum GNUNET_ErrorCode res; - - qe = find_qe (h, ntohl (msg->gns_header.r_id)); - res = ntohl (msg->ec); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received RECORD_STORE_RESPONSE with result %d\n", - res); - if (NULL == qe) - return; - if (NULL != qe->cont) - qe->cont (qe->cont_cls, res); - free_qe (qe); -} - - -/** - * Check validity of an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE - * - * @param cls - * @param msg the message we received - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -check_lookup_result (void *cls, const struct LabelLookupResponseMessage *msg) -{ - const char *name; - size_t exp_msg_len; - size_t msg_len; - size_t name_len; - size_t rd_len; - size_t key_len; - - (void) cls; - rd_len = ntohs (msg->rd_len); - msg_len = ntohs (msg->gns_header.header.size); - name_len = ntohs (msg->name_len); - key_len = ntohs (msg->key_len); - exp_msg_len = sizeof(*msg) + name_len + rd_len + key_len; - if (0 != ntohs (msg->reserved)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (msg_len != exp_msg_len) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - name = (const char *) &msg[1] + key_len; - if ((name_len > 0) && ('\0' != name[name_len - 1])) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_NO == ntohs (msg->found)) - { - if (0 != ntohs (msg->rd_count)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; - } - return check_rd (rd_len, &name[name_len], ntohs (msg->rd_count)); -} - - -/** - * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE - * - * @param cls - * @param msg the message we received - */ -static void -handle_lookup_result (void *cls, const struct LabelLookupResponseMessage *msg) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_CRYPTO_PrivateKey private_key; - const char *name; - const char *rd_tmp; - size_t name_len; - size_t rd_len; - size_t key_len; - size_t kbytes_read; - unsigned int rd_count; - int16_t found = (int16_t) ntohs (msg->found); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received RECORD_LOOKUP_RESULT (found=%i)\n", - found); - qe = find_qe (h, ntohl (msg->gns_header.r_id)); - if (NULL == qe) - return; - rd_len = ntohs (msg->rd_len); - rd_count = ntohs (msg->rd_count); - name_len = ntohs (msg->name_len); - key_len = ntohs (msg->key_len); - GNUNET_assert (GNUNET_SYSERR != - GNUNET_CRYPTO_read_private_key_from_buffer (&msg[1], - key_len, - &private_key, - &kbytes_read)); - GNUNET_assert (kbytes_read == key_len); - name = (const char *) &msg[1] + key_len; - if (GNUNET_NO == found) - { - /* label was not in namestore */ - if (NULL != qe->proc) - qe->proc (qe->proc_cls, &private_key, name, 0, NULL); - free_qe (qe); - return; - } - if (GNUNET_SYSERR == found) - { - if (NULL != qe->error_cb) - qe->error_cb (qe->error_cb_cls); - free_qe (qe); - return; - } - - rd_tmp = &name[name_len]; - { - struct GNUNET_GNSRECORD_Data rd[rd_count]; - - GNUNET_assert ( - GNUNET_OK == - GNUNET_GNSRECORD_records_deserialize (rd_len, rd_tmp, rd_count, rd)); - if (0 == name_len) - name = NULL; - if (NULL != qe->proc) - qe->proc (qe->proc_cls, - &private_key, - name, - rd_count, - (rd_count > 0) ? rd : NULL); - } - free_qe (qe); -} - - -/** - * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT - * - * @param cls - * @param msg the message we received - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -check_record_result (void *cls, const struct RecordResultMessage *msg) -{ - const char *name; - size_t msg_len; - size_t name_len; - size_t rd_len; - size_t key_len; - - (void) cls; - rd_len = ntohs (msg->rd_len); - msg_len = ntohs (msg->gns_header.header.size); - key_len = ntohs (msg->key_len); - name_len = ntohs (msg->name_len); - if (msg_len != sizeof(struct RecordResultMessage) + key_len + name_len - + rd_len) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - name = (const char *) &msg[1] + key_len; - if ((0 == name_len) || ('\0' != name[name_len - 1])) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 == key_len) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return check_rd (rd_len, &name[name_len], ntohs (msg->rd_count)); -} - - -/** - * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT - * - * @param cls - * @param msg the message we received - */ -static void -handle_record_result (void *cls, const struct RecordResultMessage *msg) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_NAMESTORE_ZoneIterator *ze; - struct GNUNET_CRYPTO_PrivateKey private_key; - const char *name; - const char *rd_tmp; - size_t name_len; - size_t rd_len; - size_t key_len; - size_t kbytes_read; - unsigned int rd_count; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received RECORD_RESULT\n"); - rd_len = ntohs (msg->rd_len); - rd_count = ntohs (msg->rd_count); - name_len = ntohs (msg->name_len); - key_len = ntohs (msg->key_len); - ze = find_zi (h, ntohl (msg->gns_header.r_id)); - qe = find_qe (h, ntohl (msg->gns_header.r_id)); - if ((NULL == ze) && (NULL == qe)) - return; /* rid not found */ - if ((NULL != ze) && (NULL != qe)) - { - GNUNET_break (0); /* rid ambiguous */ - force_reconnect (h); - return; - } - name = (const char *) &msg[1] + key_len; - GNUNET_assert (GNUNET_SYSERR != - GNUNET_CRYPTO_read_private_key_from_buffer (&msg[1], - key_len, - &private_key, - &kbytes_read)); - GNUNET_assert (kbytes_read == key_len); - rd_tmp = &name[name_len]; - { - struct GNUNET_GNSRECORD_Data rd[rd_count]; - - GNUNET_assert ( - GNUNET_OK == - GNUNET_GNSRECORD_records_deserialize (rd_len, rd_tmp, rd_count, rd)); - if (0 == name_len) - name = NULL; - if (NULL != qe) - { - if (NULL != qe->proc) - qe->proc (qe->proc_cls, - &private_key, - name, - rd_count, - (rd_count > 0) ? rd : NULL); - free_qe (qe); - return; - } - if (NULL != ze) - { - // Store them here because a callback could free ze - GNUNET_NAMESTORE_RecordMonitor proc; - GNUNET_NAMESTORE_RecordSetMonitor proc2; - void *proc_cls = ze->proc_cls; - proc = ze->proc; - proc2 = ze->proc2; - if (NULL != proc) - proc (proc_cls, &private_key, name, rd_count, rd); - if (NULL != proc2) - proc2 (proc_cls, &private_key, name, - rd_count, rd, GNUNET_TIME_absolute_ntoh (msg->expire)); - return; - } - } - GNUNET_assert (0); -} - - -/** - * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END - * - * @param cls - * @param msg the message we received - */ -static void -handle_record_result_end (void *cls, const struct GNUNET_NAMESTORE_Header *msg) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_NAMESTORE_ZoneIterator *ze; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received RECORD_RESULT_END\n"); - ze = find_zi (h, ntohl (msg->r_id)); - qe = find_qe (h, ntohl (msg->r_id)); - if ((NULL == ze) && (NULL == qe)) - return; /* rid not found */ - if ((NULL != ze) && (NULL != qe)) - { - GNUNET_break (0); /* rid ambiguous */ - force_reconnect (h); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Zone iteration completed!\n"); - if (NULL == ze) - { - GNUNET_break (0); - force_reconnect (h); - return; - } - if (NULL != ze->finish_cb) - ze->finish_cb (ze->finish_cb_cls); - free_ze (ze); -} - -static void -handle_tx_control_result (void *cls, - const struct TxControlResultMessage *msg) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - struct GNUNET_NAMESTORE_QueueEntry *qe; - enum GNUNET_ErrorCode res; - - qe = find_qe (h, ntohl (msg->gns_header.r_id)); - res = ntohs (msg->ec); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received TX_CONTROL_RESULT with result %d\n", - res); - if (NULL == qe) - return; - if (NULL != qe->cont) - qe->cont (qe->cont_cls, res); - free_qe (qe); -} - -/** - * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE. - * - * @param qe the respective entry in the message queue - * @param msg the message we received - * @return #GNUNET_OK on success, #GNUNET_SYSERR if message malformed - */ -static int -check_zone_to_name_response (void *cls, - const struct ZoneToNameResponseMessage *msg) -{ - size_t name_len; - size_t rd_ser_len; - size_t key_len; - const char *name_tmp; - - (void) cls; - if (GNUNET_EC_NONE != ntohl (msg->ec)) - return GNUNET_OK; - key_len = ntohs (msg->key_len); - name_len = ntohs (msg->name_len); - rd_ser_len = ntohs (msg->rd_len); - if (ntohs (msg->gns_header.header.size) != - sizeof(struct ZoneToNameResponseMessage) + key_len + name_len - + rd_ser_len) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - name_tmp = (const char *) &msg[1] + key_len; - if ((name_len > 0) && ('\0' != name_tmp[name_len - 1])) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return check_rd (rd_ser_len, &name_tmp[name_len], ntohs (msg->rd_count)); -} - - -/** - * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE. - * - * @param cls - * @param msg the message we received - */ -static void -handle_zone_to_name_response (void *cls, - const struct ZoneToNameResponseMessage *msg) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_CRYPTO_PrivateKey zone; - enum GNUNET_ErrorCode res; - size_t name_len; - size_t rd_ser_len; - unsigned int rd_count; - const char *name_tmp; - const char *rd_tmp; - size_t key_len; - size_t kbytes_read; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received ZONE_TO_NAME_RESPONSE\n"); - qe = find_qe (h, ntohl (msg->gns_header.r_id)); - if (NULL == qe) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Response queue already gone...\n"); - return; - } - res = ntohl (msg->ec); - key_len = ntohs (msg->key_len); - GNUNET_assert (GNUNET_SYSERR != - GNUNET_CRYPTO_read_private_key_from_buffer (&msg[1], - key_len, - &zone, - &kbytes_read)); - GNUNET_assert (kbytes_read == key_len); - switch (res) - { - break; - - case GNUNET_EC_NAMESTORE_NO_RESULTS: - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Namestore has no result for zone to name mapping \n"); - if (NULL != qe->proc) - qe->proc (qe->proc_cls, &zone, NULL, 0, NULL); - free_qe (qe); - return; - - case GNUNET_EC_NONE: - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Namestore has result for zone to name mapping \n"); - name_len = ntohs (msg->name_len); - rd_count = ntohs (msg->rd_count); - rd_ser_len = ntohs (msg->rd_len); - name_tmp = (const char *) &msg[1] + key_len; - rd_tmp = &name_tmp[name_len]; - { - struct GNUNET_GNSRECORD_Data rd[rd_count]; - - GNUNET_assert (GNUNET_OK == - GNUNET_GNSRECORD_records_deserialize (rd_ser_len, - rd_tmp, - rd_count, - rd)); - /* normal end, call continuation with result */ - if (NULL != qe->proc) - qe->proc (qe->proc_cls, &zone, name_tmp, rd_count, rd); - /* return is important here: break would call continuation with error! */ - free_qe (qe); - return; - } - - default: - LOG (GNUNET_ERROR_TYPE_DEBUG, - "An error occurred during zone to name operation: %s\n", - GNUNET_ErrorCode_get_hint (res)); - break; - } - /* error case, call continuation with error */ - if (NULL != qe->error_cb) - qe->error_cb (qe->error_cb_cls); - free_qe (qe); -} - - -/** - * Generic error handler, called with the appropriate error code and - * the same closure specified at the creation of the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls closure with the `struct GNUNET_NAMESTORE_Handle *` - * @param error error code - */ -static void -mq_error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - - (void) error; - force_reconnect (h); -} - - -/** - * Reconnect to namestore service. - * - * @param h the handle to the NAMESTORE service - */ -static void -reconnect (struct GNUNET_NAMESTORE_Handle *h) -{ - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_fixed_size (record_store_response, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE, - struct RecordStoreResponseMessage, - h), - GNUNET_MQ_hd_var_size (zone_to_name_response, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE, - struct ZoneToNameResponseMessage, - h), - GNUNET_MQ_hd_var_size (record_result, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT, - struct RecordResultMessage, - h), - GNUNET_MQ_hd_fixed_size (record_result_end, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END, - struct GNUNET_NAMESTORE_Header, - h), - GNUNET_MQ_hd_var_size (lookup_result, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE, - struct LabelLookupResponseMessage, - h), - GNUNET_MQ_hd_fixed_size (tx_control_result, - GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL_RESULT, - struct TxControlResultMessage, - h), - GNUNET_MQ_handler_end () }; - struct GNUNET_NAMESTORE_ZoneIterator *it; - struct GNUNET_NAMESTORE_QueueEntry *qe; - - GNUNET_assert (NULL == h->mq); - h->mq = - GNUNET_CLIENT_connect (h->cfg, "namestore", handlers, &mq_error_handler, h); - if (NULL == h->mq) - return; - /* re-transmit pending requests that waited for a reconnect... */ - for (it = h->z_head; NULL != it; it = it->next) - { - GNUNET_MQ_send (h->mq, it->env); - it->env = NULL; - } - for (qe = h->op_head; NULL != qe; qe = qe->next) - { - GNUNET_MQ_send (h->mq, qe->env); - qe->env = NULL; - } -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -reconnect_task (void *cls) -{ - struct GNUNET_NAMESTORE_Handle *h = cls; - - h->reconnect_task = NULL; - reconnect (h); -} - - -/** - * Disconnect from service and then reconnect. - * - * @param h our handle - */ -static void -force_reconnect (struct GNUNET_NAMESTORE_Handle *h) -{ - struct GNUNET_NAMESTORE_ZoneIterator *ze; - struct GNUNET_NAMESTORE_QueueEntry *qe; - - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - while (NULL != (ze = h->z_head)) - { - if (NULL != ze->error_cb) - ze->error_cb (ze->error_cb_cls); - free_ze (ze); - } - while (NULL != (qe = h->op_head)) - { - if (NULL != qe->error_cb) - qe->error_cb (qe->error_cb_cls); - if (NULL != qe->cont) - qe->cont (qe->cont_cls, - GNUNET_EC_NAMESTORE_UNKNOWN); - free_qe (qe); - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting to namestore\n"); - h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); - h->reconnect_task = - GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect_task, h); -} - - -/** - * Get a fresh operation id to distinguish between namestore requests - * - * @param h the namestore handle - * @return next operation id to use - */ -static uint32_t -get_op_id (struct GNUNET_NAMESTORE_Handle *h) -{ - return h->last_op_id_used++; -} - - -/** - * Initialize the connection with the NAMESTORE service. - * - * @param cfg configuration to use - * @return handle to the GNS service, or NULL on error - */ -struct GNUNET_NAMESTORE_Handle * -GNUNET_NAMESTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_NAMESTORE_Handle *h; - - h = GNUNET_new (struct GNUNET_NAMESTORE_Handle); - h->cfg = cfg; - reconnect (h); - if (NULL == h->mq) - { - GNUNET_free (h); - return NULL; - } - return h; -} - - -/** - * Disconnect from the namestore service (and free associated - * resources). - * - * @param h handle to the namestore - */ -void -GNUNET_NAMESTORE_disconnect (struct GNUNET_NAMESTORE_Handle *h) -{ - struct GNUNET_NAMESTORE_QueueEntry *q; - struct GNUNET_NAMESTORE_ZoneIterator *z; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n"); - GNUNET_break (NULL == h->op_head); - while (NULL != (q = h->op_head)) - { - GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, q); - GNUNET_free (q); - } - GNUNET_break (NULL == h->z_head); - while (NULL != (z = h->z_head)) - { - GNUNET_CONTAINER_DLL_remove (h->z_head, h->z_tail, z); - GNUNET_free (z); - } - if (NULL != h->mq) - { - GNUNET_MQ_destroy (h->mq); - h->mq = NULL; - } - if (NULL != h->reconnect_task) - { - GNUNET_SCHEDULER_cancel (h->reconnect_task); - h->reconnect_task = NULL; - } - GNUNET_free (h); -} - - -/** - * Task launched to warn the user that the namestore is - * excessively slow and that a query was thus dropped. - * - * @param cls a `struct GNUNET_NAMESTORE_QueueEntry *` - */ -static void -warn_delay (void *cls) -{ - struct GNUNET_NAMESTORE_QueueEntry *qe = cls; - - qe->timeout_task = NULL; - LOG (GNUNET_ERROR_TYPE_WARNING, - "Did not receive response from namestore after %s!\n", - GNUNET_STRINGS_relative_time_to_string (NAMESTORE_DELAY_TOLERANCE, - GNUNET_YES)); - if (NULL != qe->cont) - { - qe->cont (qe->cont_cls, GNUNET_EC_NAMESTORE_UNKNOWN); - qe->cont = NULL; - } - GNUNET_NAMESTORE_cancel (qe); -} - - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_records_store ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *pkey, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd, - GNUNET_NAMESTORE_ContinuationWithStatus cont, - void *cont_cls) -{ - struct GNUNET_NAMESTORE_RecordInfo ri; - unsigned int rds_sent; - ri.a_label = label; - ri.a_rd_count = rd_count; - ri.a_rd = (struct GNUNET_GNSRECORD_Data *) rd; - return GNUNET_NAMESTORE_records_store2 (h, pkey, 1, &ri, &rds_sent, - cont, cont_cls); -} - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_records_store2 ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *pkey, - unsigned int rd_set_count, - const struct GNUNET_NAMESTORE_RecordInfo *record_info, - unsigned int *rds_sent, - GNUNET_NAMESTORE_ContinuationWithStatus cont, - void *cont_cls) -{ - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_MQ_Envelope *env; - const char *label; - unsigned int rd_count; - const struct GNUNET_GNSRECORD_Data *rd; - char *name_tmp; - char *rd_ser; - ssize_t rd_ser_len[rd_set_count]; - size_t name_len; - uint32_t rid; - struct RecordStoreMessage *msg; - struct RecordSet *rd_set; - ssize_t sret; - int i; - size_t rd_set_len = 0; - size_t key_len = 0; - size_t max_len; - key_len = GNUNET_CRYPTO_private_key_get_length (pkey); - max_len = UINT16_MAX - key_len - sizeof (struct RecordStoreMessage); - - *rds_sent = 0; - for (i = 0; i < rd_set_count; i++) - { - label = record_info[i].a_label; - rd_count = record_info[i].a_rd_count; - rd = record_info[i].a_rd; - name_len = strlen (label) + 1; - if (name_len > MAX_NAME_LEN) - { - GNUNET_break (0); - *rds_sent = 0; - return NULL; - } - rd_ser_len[i] = GNUNET_GNSRECORD_records_get_size (rd_count, rd); - if (rd_ser_len[i] < 0) - { - GNUNET_break (0); - *rds_sent = 0; - return NULL; - } - if (rd_ser_len[i] > max_len) - { - GNUNET_break (0); - *rds_sent = 0; - return NULL; - } - if ((rd_set_len + sizeof (struct RecordSet) + name_len + rd_ser_len[i]) > - max_len) - break; - rd_set_len += sizeof (struct RecordSet) + name_len + rd_ser_len[i]; - } - *rds_sent = i; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending %u of %u records!\n", *rds_sent, rd_set_count); - rid = get_op_id (h); - qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); - qe->h = h; - qe->cont = cont; - qe->cont_cls = cont_cls; - qe->op_id = rid; - GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); - /* setup msg */ - env = GNUNET_MQ_msg_extra (msg, - key_len + rd_set_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE); - GNUNET_assert (NULL != msg); - GNUNET_assert (NULL != env); - msg->gns_header.r_id = htonl (rid); - msg->key_len = htons (key_len); - msg->rd_set_count = htons ((uint16_t) (*rds_sent)); - GNUNET_CRYPTO_write_private_key_to_buffer (pkey, - &msg[1], - key_len); - rd_set = (struct RecordSet*) (((char*) &msg[1]) + key_len); - for (int i = 0; i < *rds_sent; i++) - { - label = record_info[i].a_label; - rd = record_info[i].a_rd; - name_len = strlen (label) + 1; - rd_set->name_len = htons (name_len); - rd_set->rd_count = htons (record_info[i].a_rd_count); - rd_set->rd_len = htons (rd_ser_len[i]); - rd_set->reserved = ntohs (0); - name_tmp = (char *) &rd_set[1]; - GNUNET_memcpy (name_tmp, label, name_len); - rd_ser = &name_tmp[name_len]; - sret = GNUNET_GNSRECORD_records_serialize (record_info[i].a_rd_count, - rd, rd_ser_len[i], rd_ser); - if ((0 > sret) || (sret != rd_ser_len[i])) - { - GNUNET_break (0); - GNUNET_free (env); - return NULL; - } - // Point to next RecordSet - rd_set = (struct RecordSet*) &name_tmp[name_len + rd_ser_len[i]]; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending NAMESTORE_RECORD_STORE message for name %u record sets\n", - *rds_sent); - qe->timeout_task = - GNUNET_SCHEDULER_add_delayed (NAMESTORE_DELAY_TOLERANCE, &warn_delay, qe); - if (NULL == h->mq) - { - qe->env = env; - LOG (GNUNET_ERROR_TYPE_WARNING, - "Delaying NAMESTORE_RECORD_STORE message as namestore is not ready!\n"); - } - else - { - GNUNET_MQ_send (h->mq, env); - } - return qe; -} - - -static struct GNUNET_NAMESTORE_QueueEntry * -records_lookup ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *pkey, - const char *label, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordMonitor rm, - void *rm_cls, - int is_edit_request, - enum GNUNET_GNSRECORD_Filter filter) -{ - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_MQ_Envelope *env; - struct LabelLookupMessage *msg; - size_t label_len; - size_t key_len; - - if (1 == (label_len = strlen (label) + 1)) - { - GNUNET_break (0); - return NULL; - } - - qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); - qe->h = h; - qe->error_cb = error_cb; - qe->error_cb_cls = error_cb_cls; - qe->proc = rm; - qe->proc_cls = rm_cls; - qe->op_id = get_op_id (h); - GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); - - key_len = GNUNET_CRYPTO_private_key_get_length (pkey); - env = GNUNET_MQ_msg_extra (msg, - label_len + key_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP); - msg->gns_header.r_id = htonl (qe->op_id); - GNUNET_CRYPTO_write_private_key_to_buffer (pkey, - &msg[1], - key_len); - - msg->key_len = htons (key_len); - msg->is_edit_request = htons (is_edit_request); - msg->label_len = htons (label_len); - msg->filter = htons (filter); - GNUNET_memcpy (((char*) &msg[1]) + key_len, label, label_len); - if (NULL == h->mq) - qe->env = env; - else - GNUNET_MQ_send (h->mq, env); - return qe; -} - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_records_lookup ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *pkey, - const char *label, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordMonitor rm, - void *rm_cls) -{ - return records_lookup (h, pkey, label, - error_cb, error_cb_cls, - rm, rm_cls, GNUNET_NO, GNUNET_GNSRECORD_FILTER_NONE); - -} - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_records_lookup2 ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *pkey, - const char *label, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordMonitor rm, - void *rm_cls, - enum GNUNET_GNSRECORD_Filter filter) -{ - return records_lookup (h, pkey, label, - error_cb, error_cb_cls, - rm, rm_cls, GNUNET_NO, filter); - -} - - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_records_edit ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *pkey, - const char *label, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordMonitor rm, - void *rm_cls) -{ - return records_lookup (h, pkey, label, - error_cb, error_cb_cls, - rm, rm_cls, GNUNET_YES, GNUNET_GNSRECORD_FILTER_NONE); -} - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_zone_to_name ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const struct GNUNET_CRYPTO_PublicKey *value_zone, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordMonitor proc, - void *proc_cls) -{ - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_MQ_Envelope *env; - struct ZoneToNameMessage *msg; - uint32_t rid; - size_t key_len; - ssize_t pkey_len; - - rid = get_op_id (h); - qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); - qe->h = h; - qe->error_cb = error_cb; - qe->error_cb_cls = error_cb_cls; - qe->proc = proc; - qe->proc_cls = proc_cls; - qe->op_id = rid; - GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); - - key_len = GNUNET_CRYPTO_private_key_get_length (zone); - pkey_len = GNUNET_CRYPTO_public_key_get_length (value_zone); - env = GNUNET_MQ_msg_extra (msg, key_len + pkey_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME); - msg->gns_header.r_id = htonl (rid); - msg->key_len = htons (key_len); - msg->pkey_len = htons (pkey_len); - GNUNET_CRYPTO_write_private_key_to_buffer (zone, &msg[1], key_len); - GNUNET_CRYPTO_write_public_key_to_buffer (value_zone, - (char*) &msg[1] + key_len, - pkey_len); - if (NULL == h->mq) - qe->env = env; - else - GNUNET_MQ_send (h->mq, env); - return qe; -} - - -struct GNUNET_NAMESTORE_ZoneIterator * -GNUNET_NAMESTORE_zone_iteration_start ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *zone, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordMonitor proc, - void *proc_cls, - GNUNET_SCHEDULER_TaskCallback finish_cb, - void *finish_cb_cls) -{ - struct GNUNET_NAMESTORE_ZoneIterator *it; - struct GNUNET_MQ_Envelope *env; - struct ZoneIterationStartMessage *msg; - uint32_t rid; - size_t key_len = 0; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending ZONE_ITERATION_START message\n"); - rid = get_op_id (h); - it = GNUNET_new (struct GNUNET_NAMESTORE_ZoneIterator); - it->h = h; - it->error_cb = error_cb; - it->error_cb_cls = error_cb_cls; - it->finish_cb = finish_cb; - it->finish_cb_cls = finish_cb_cls; - it->proc = proc; - it->proc_cls = proc_cls; - it->op_id = rid; - if (NULL != zone) - { - it->zone = *zone; - key_len = GNUNET_CRYPTO_private_key_get_length (zone); - } - GNUNET_CONTAINER_DLL_insert_tail (h->z_head, h->z_tail, it); - env = GNUNET_MQ_msg_extra (msg, - key_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START); - msg->gns_header.r_id = htonl (rid); - msg->key_len = htons (key_len); - if (NULL != zone) - GNUNET_CRYPTO_write_private_key_to_buffer (zone, &msg[1], key_len); - if (NULL == h->mq) - it->env = env; - else - GNUNET_MQ_send (h->mq, env); - return it; -} - -struct GNUNET_NAMESTORE_ZoneIterator * -GNUNET_NAMESTORE_zone_iteration_start2 ( - struct GNUNET_NAMESTORE_Handle *h, - const struct GNUNET_CRYPTO_PrivateKey *zone, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordSetMonitor proc, - void *proc_cls, - GNUNET_SCHEDULER_TaskCallback finish_cb, - void *finish_cb_cls, - enum GNUNET_GNSRECORD_Filter filter) -{ - struct GNUNET_NAMESTORE_ZoneIterator *it; - struct GNUNET_MQ_Envelope *env; - struct ZoneIterationStartMessage *msg; - uint32_t rid; - size_t key_len = 0; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending ZONE_ITERATION_START message\n"); - rid = get_op_id (h); - it = GNUNET_new (struct GNUNET_NAMESTORE_ZoneIterator); - it->h = h; - it->error_cb = error_cb; - it->error_cb_cls = error_cb_cls; - it->finish_cb = finish_cb; - it->finish_cb_cls = finish_cb_cls; - it->proc2 = proc; - it->proc_cls = proc_cls; - it->op_id = rid; - if (NULL != zone) - { - it->zone = *zone; - key_len = GNUNET_CRYPTO_private_key_get_length (zone); - } - GNUNET_CONTAINER_DLL_insert_tail (h->z_head, h->z_tail, it); - env = GNUNET_MQ_msg_extra (msg, - key_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START); - msg->gns_header.r_id = htonl (rid); - msg->key_len = htons (key_len); - msg->filter = htons ((uint16_t) filter); - if (NULL != zone) - GNUNET_CRYPTO_write_private_key_to_buffer (zone, &msg[1], key_len); - if (NULL == h->mq) - it->env = env; - else - GNUNET_MQ_send (h->mq, env); - return it; -} - - -void -GNUNET_NAMESTORE_zone_iterator_next (struct GNUNET_NAMESTORE_ZoneIterator *it, - uint64_t limit) -{ - struct GNUNET_NAMESTORE_Handle *h = it->h; - struct ZoneIterationNextMessage *msg; - struct GNUNET_MQ_Envelope *env; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending ZONE_ITERATION_NEXT message with limit %llu\n", - (unsigned long long) limit); - env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT); - msg->gns_header.r_id = htonl (it->op_id); - msg->limit = GNUNET_htonll (limit); - GNUNET_MQ_send (h->mq, env); -} - - -/** - * Stops iteration and releases the namestore handle for further calls. - * - * @param it the iterator - */ -void -GNUNET_NAMESTORE_zone_iteration_stop (struct GNUNET_NAMESTORE_ZoneIterator *it) -{ - struct GNUNET_NAMESTORE_Handle *h = it->h; - struct GNUNET_MQ_Envelope *env; - struct ZoneIterationStopMessage *msg; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending ZONE_ITERATION_STOP message\n"); - if (NULL != h->mq) - { - env = - GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP); - msg->gns_header.r_id = htonl (it->op_id); - GNUNET_MQ_send (h->mq, env); - } - free_ze (it); -} - - -/** - * Cancel a namestore operation. The final callback from the - * operation must not have been done yet. - * - * @param qe operation to cancel - */ -void -GNUNET_NAMESTORE_cancel (struct GNUNET_NAMESTORE_QueueEntry *qe) -{ - free_qe (qe); -} - -/** - * New API draft. Experimental - */ - -static struct GNUNET_NAMESTORE_QueueEntry * -send_transaction_control_msg (struct GNUNET_NAMESTORE_Handle *h, - GNUNET_NAMESTORE_ContinuationWithStatus cont, - void *cont_cls, - enum GNUNET_NAMESTORE_TxControl ctrl) -{ - struct GNUNET_NAMESTORE_QueueEntry *qe; - struct GNUNET_MQ_Envelope *env; - struct TxControlMessage *msg; - uint32_t rid; - - rid = get_op_id (h); - qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); - qe->h = h; - qe->cont = cont; - qe->cont_cls = cont_cls; - qe->op_id = rid; - GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); - - env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL); - msg->gns_header.r_id = htonl (rid); - msg->control = htons (ctrl); - if (NULL == h->mq) - qe->env = env; - else - GNUNET_MQ_send (h->mq, env); - return qe; - GNUNET_break (0); - return NULL; -} - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_transaction_begin (struct GNUNET_NAMESTORE_Handle *h, - GNUNET_NAMESTORE_ContinuationWithStatus cont, - void *cont_cls) -{ - return send_transaction_control_msg (h, cont, cont_cls, - GNUNET_NAMESTORE_TX_BEGIN); -} - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_transaction_commit (struct GNUNET_NAMESTORE_Handle *h, - GNUNET_NAMESTORE_ContinuationWithStatus - cont, - void *cont_cls) -{ - return send_transaction_control_msg (h, cont, cont_cls, - GNUNET_NAMESTORE_TX_COMMIT); -} - - -struct GNUNET_NAMESTORE_QueueEntry * -GNUNET_NAMESTORE_transaction_rollback (struct GNUNET_NAMESTORE_Handle *h, - GNUNET_NAMESTORE_ContinuationWithStatus - cont, - void *cont_cls) -{ - return send_transaction_control_msg (h, cont, cont_cls, - GNUNET_NAMESTORE_TX_ROLLBACK); -} - - -/* end of namestore_api.c */ diff --git a/src/namestore/namestore_api_monitor.c b/src/namestore/namestore_api_monitor.c deleted file mode 100644 index ec4ba879b..000000000 --- a/src/namestore/namestore_api_monitor.c +++ /dev/null @@ -1,443 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2016, 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/namestore_api_monitor.c - * @brief API to monitor changes in the NAMESTORE - * @author Christian Grothoff - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_constants.h" -#include "gnunet_arm_service.h" -#include "gnunet_signatures.h" -#include "gnunet_namestore_service.h" -#include "namestore.h" - - -/** - * Handle for a monitoring activity. - */ -struct GNUNET_NAMESTORE_ZoneMonitor -{ - /** - * Configuration (to reconnect). - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Handle to namestore service. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Function to call on errors. - */ - GNUNET_SCHEDULER_TaskCallback error_cb; - - /** - * Closure for @e error_cb. - */ - void *error_cb_cls; - - /** - * Function to call on events. - */ - GNUNET_NAMESTORE_RecordMonitor monitor; - - /** - * Function to call on events. - */ - GNUNET_NAMESTORE_RecordSetMonitor monitor2; - - /** - * Record set filter for this monitor - */ - enum GNUNET_GNSRECORD_Filter filter; - - /** - * Closure for @e monitor. - */ - void *monitor_cls; - - /** - * Function called when we've synchronized. - */ - GNUNET_SCHEDULER_TaskCallback sync_cb; - - /** - * Closure for @e sync_cb. - */ - void *sync_cb_cls; - - /** - * Monitored zone. - */ - struct GNUNET_CRYPTO_PrivateKey zone; - - /** - * Do we first iterate over all existing records? - */ - int iterate_first; - - /** - * Zone key length - */ - uint32_t key_len; -}; - - -/** - * Reconnect to the namestore service. - * - * @param zm monitor to reconnect - */ -static void -reconnect (struct GNUNET_NAMESTORE_ZoneMonitor *zm); - - -/** - * Handle SYNC message from the namestore service. - * - * @param cls the monitor - * @param msg the sync message - */ -static void -handle_sync (void *cls, const struct GNUNET_MessageHeader *msg) -{ - struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; - - (void) cls; - (void) msg; - if (NULL != zm->sync_cb) - zm->sync_cb (zm->sync_cb_cls); -} - - -/** - * We've received a notification about a change to our zone. - * Check that it is well-formed. - * - * @param cls the zone monitor handle - * @param lrm the message from the service. - */ -static int -check_result (void *cls, const struct RecordResultMessage *lrm) -{ - struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; - size_t lrm_len; - size_t exp_lrm_len; - size_t name_len; - size_t rd_len; - unsigned rd_count; - const char *name_tmp; - const char *rd_ser_tmp; - size_t key_len; - - (void) zm; - key_len = ntohs (lrm->key_len); - (void) cls; - if (0 == key_len) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - lrm_len = ntohs (lrm->gns_header.header.size); - rd_len = ntohs (lrm->rd_len); - rd_count = ntohs (lrm->rd_count); - name_len = ntohs (lrm->name_len); - if (name_len > MAX_NAME_LEN) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - exp_lrm_len = sizeof(struct RecordResultMessage) + name_len + rd_len + key_len; - if (lrm_len != exp_lrm_len) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (0 == name_len) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - name_tmp = (const char *) &lrm[1] + key_len; - if (name_tmp[name_len - 1] != '\0') - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - rd_ser_tmp = (const char *) &name_tmp[name_len]; - { - struct GNUNET_GNSRECORD_Data rd[rd_count]; - - if (GNUNET_OK != - GNUNET_GNSRECORD_records_deserialize (rd_len, rd_ser_tmp, rd_count, rd)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -/** - * We've received a notification about a change to our zone. - * Forward to monitor callback. - * - * @param cls the zone monitor handle - * @param lrm the message from the service. - */ -static void -handle_result (void *cls, const struct RecordResultMessage *lrm) -{ - struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; - struct GNUNET_CRYPTO_PrivateKey private_key; - size_t name_len; - size_t rd_len; - size_t key_len; - size_t kbytes_read; - unsigned rd_count; - const char *name_tmp; - const char *rd_ser_tmp; - - key_len = ntohs (lrm->key_len); - rd_len = ntohs (lrm->rd_len); - rd_count = ntohs (lrm->rd_count); - name_len = ntohs (lrm->name_len); - name_tmp = (const char *) &lrm[1] + key_len; - GNUNET_assert (GNUNET_SYSERR != - GNUNET_CRYPTO_read_private_key_from_buffer (&lrm[1], - key_len, - &private_key, - &kbytes_read)); - GNUNET_assert (kbytes_read == key_len); - rd_ser_tmp = (const char *) &name_tmp[name_len]; - { - struct GNUNET_GNSRECORD_Data rd[rd_count]; - - GNUNET_assert ( - GNUNET_OK == - GNUNET_GNSRECORD_records_deserialize (rd_len, rd_ser_tmp, rd_count, rd)); - if (NULL != zm->monitor2) - zm->monitor2 (zm->monitor_cls, &private_key, name_tmp, - rd_count, rd, GNUNET_TIME_absolute_ntoh (lrm->expire)); - else - zm->monitor (zm->monitor_cls, &private_key, name_tmp, rd_count, rd); - } -} - - -/** - * Generic error handler, called with the appropriate error code and - * the same closure specified at the creation of the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls closure with the `struct GNUNET_NAMESTORE_ZoneMonitor *` - * @param error error code - */ -static void -mq_error_handler (void *cls, enum GNUNET_MQ_Error error) -{ - struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; - - (void) error; - reconnect (zm); -} - - -/** - * Reconnect to the namestore service. - * - * @param zm monitor to reconnect - */ -static void -reconnect (struct GNUNET_NAMESTORE_ZoneMonitor *zm) -{ - struct GNUNET_MQ_MessageHandler handlers[] = - { GNUNET_MQ_hd_fixed_size (sync, - GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_SYNC, - struct GNUNET_MessageHeader, - zm), - GNUNET_MQ_hd_var_size (result, - GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT, - struct RecordResultMessage, - zm), - GNUNET_MQ_handler_end () }; - struct GNUNET_MQ_Envelope *env; - struct ZoneMonitorStartMessage *sm; - - if (NULL != zm->mq) - { - GNUNET_MQ_destroy (zm->mq); - zm->error_cb (zm->error_cb_cls); - } - zm->mq = GNUNET_CLIENT_connect (zm->cfg, - "namestore", - handlers, - &mq_error_handler, - zm); - if (NULL == zm->mq) - return; - env = GNUNET_MQ_msg_extra (sm, - zm->key_len, - GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START); - sm->iterate_first = htonl (zm->iterate_first); - if (0 < zm->key_len) - GNUNET_CRYPTO_write_private_key_to_buffer (&zm->zone, - &sm[1], - zm->key_len); - sm->key_len = htons (zm->key_len); - sm->filter = htons (zm->filter); - GNUNET_MQ_send (zm->mq, env); -} - - -struct GNUNET_NAMESTORE_ZoneMonitor * -GNUNET_NAMESTORE_zone_monitor_start ( - const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct GNUNET_CRYPTO_PrivateKey *zone, - int iterate_first, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordMonitor monitor, - void *monitor_cls, - GNUNET_SCHEDULER_TaskCallback sync_cb, - void *sync_cb_cls) -{ - struct GNUNET_NAMESTORE_ZoneMonitor *zm; - - zm = GNUNET_new (struct GNUNET_NAMESTORE_ZoneMonitor); - if (NULL != zone) - { - zm->key_len = GNUNET_CRYPTO_private_key_get_length (zone); - zm->zone = *zone; - } - zm->iterate_first = iterate_first; - zm->error_cb = error_cb; - zm->error_cb_cls = error_cb_cls; - zm->monitor = monitor; - zm->monitor_cls = monitor_cls; - zm->sync_cb = sync_cb; - zm->sync_cb_cls = sync_cb_cls; - zm->cfg = cfg; - reconnect (zm); - if (NULL == zm->mq) - { - GNUNET_free (zm); - return NULL; - } - return zm; -} - -struct GNUNET_NAMESTORE_ZoneMonitor * -GNUNET_NAMESTORE_zone_monitor_start2 ( - const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct GNUNET_CRYPTO_PrivateKey *zone, - int iterate_first, - GNUNET_SCHEDULER_TaskCallback error_cb, - void *error_cb_cls, - GNUNET_NAMESTORE_RecordSetMonitor monitor, - void *monitor_cls, - GNUNET_SCHEDULER_TaskCallback sync_cb, - void *sync_cb_cls, - enum GNUNET_GNSRECORD_Filter filter) -{ - struct GNUNET_NAMESTORE_ZoneMonitor *zm; - - zm = GNUNET_new (struct GNUNET_NAMESTORE_ZoneMonitor); - if (NULL != zone) - { - zm->key_len = GNUNET_CRYPTO_private_key_get_length (zone); - zm->zone = *zone; - } - zm->iterate_first = iterate_first; - zm->error_cb = error_cb; - zm->error_cb_cls = error_cb_cls; - zm->monitor2 = monitor; - zm->monitor_cls = monitor_cls; - zm->sync_cb = sync_cb; - zm->sync_cb_cls = sync_cb_cls; - zm->cfg = cfg; - zm->filter = filter; - reconnect (zm); - if (NULL == zm->mq) - { - GNUNET_free (zm); - return NULL; - } - return zm; -} - - -/** - * Calls the monitor processor specified in #GNUNET_NAMESTORE_zone_monitor_start - * for the next record(s). This function is used to allow clients that merely - * monitor the NAMESTORE to still throttle namestore operations, so we can be - * sure that the monitors can keep up. - * - * Note that #GNUNET_NAMESTORE_records_store() only waits for this - * call if the previous limit set by the client was already reached. - * Thus, by using a @a limit greater than 1, monitors basically enable - * a queue of notifications to be processed asynchronously with some - * delay. Note that even with a limit of 1 the - * #GNUNET_NAMESTORE_records_store() function will run asynchronously - * and the continuation may be invoked before the monitors completed - * (or even started) processing the notification. Thus, monitors will - * only closely track the current state of the namestore, but not - * be involved in the transactions. - * - * @param zm the monitor - * @param limit number of records to return to the iterator in one shot - * (before #GNUNET_NAMESTORE_zone_monitor_next is to be called again) - */ -void -GNUNET_NAMESTORE_zone_monitor_next (struct GNUNET_NAMESTORE_ZoneMonitor *zm, - uint64_t limit) -{ - struct GNUNET_MQ_Envelope *env; - struct ZoneMonitorNextMessage *nm; - - env = GNUNET_MQ_msg (nm, GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT); - nm->limit = GNUNET_htonll (limit); - GNUNET_MQ_send (zm->mq, env); -} - - -/** - * Stop monitoring a zone for changes. - * - * @param zm handle to the monitor activity to stop - */ -void -GNUNET_NAMESTORE_zone_monitor_stop (struct GNUNET_NAMESTORE_ZoneMonitor *zm) -{ - if (NULL != zm->mq) - { - GNUNET_MQ_destroy (zm->mq); - zm->mq = NULL; - } - GNUNET_free (zm); -} - - -/* end of namestore_api_monitor.c */ diff --git a/src/namestore/perf_namestore_api_import.c b/src/namestore/perf_namestore_api_import.c deleted file mode 100644 index e56fb961c..000000000 --- a/src/namestore/perf_namestore_api_import.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2022 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/perf_namestore_api_import.c - * @brief testcase for namestore: Import a lot of records - * @author Martin Schanzenbach - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TEST_RECORD_COUNT 10000 - -/** - * A #BENCHMARK_SIZE of 1000 takes less than a minute on a reasonably - * modern system, so 30 minutes should be OK even for very, very - * slow systems. - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30) - -/** - * The runtime of the benchmark is expected to be linear - * for the iteration phase with a *good* database. The FLAT - * database uses a quadratic retrieval algorithm, - * hence it should be quadratic in the size. - */ -#define BENCHMARK_SIZE 1000 - -/** - * Maximum record size - */ -#define MAX_REC_SIZE 500 - -/** - * How big are the blocks we fetch? Note that the first block is - * always just 1 record set per current API. Smaller block - * sizes will make quadratic iteration-by-offset penalties - * more pronounced. - */ -#define BLOCK_SIZE 100 - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *timeout_task; - -static struct GNUNET_SCHEDULER_Task *t; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_NAMESTORE_QueueEntry *qe; - -static int res; - -static struct GNUNET_TIME_Absolute start; - -struct GNUNET_NAMESTORE_RecordInfo ri[TEST_RECORD_COUNT]; - -int single_put_pos; - -static int bulk_count = 0; - - -/** - * Terminate everything - * - * @param cls NULL - */ -static void -end (void *cls) -{ - (void) cls; - if (NULL != qe) - { - GNUNET_NAMESTORE_cancel (qe); - qe = NULL; - } - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - if (NULL != t) - { - GNUNET_SCHEDULER_cancel (t); - t = NULL; - } - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } -} - - -/** - * End with timeout. As this is a benchmark, we do not - * fail hard but return "skipped". - */ -static void -timeout (void *cls) -{ - (void) cls; - timeout_task = NULL; - GNUNET_SCHEDULER_shutdown (); - res = 77; -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_malloc (count + sizeof(struct GNUNET_GNSRECORD_Data)); - rd->expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd->record_type = TEST_RECORD_TYPE; - rd->data_size = count; - rd->data = (void *) &rd[1]; - rd->flags = 0; - memset (&rd[1], - 'a', - count); - return rd; -} - - -static void -publish_records_single (void *cls); - -static void -commit_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - struct GNUNET_TIME_Relative delay; - - (void) cls; - qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - single_put_pos++; - delay = GNUNET_TIME_absolute_get_duration (start); - fprintf (stdout, - "BULK-TX: Publishing %u records took %s\n", - TEST_RECORD_COUNT, - GNUNET_STRINGS_relative_time_to_string (delay, - GNUNET_YES)); - res = 0; - GNUNET_SCHEDULER_shutdown (); -} - -static void -publish_records_bulk_tx (void *cls); - - -static void -put_cont_bulk_tx (void *cls, - enum GNUNET_ErrorCode ec) -{ - qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - if (bulk_count == TEST_RECORD_COUNT) - { - qe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont, NULL); - return; - } - t = GNUNET_SCHEDULER_add_now (&publish_records_bulk_tx, NULL); -} - - -static void -publish_records_bulk_tx (void *cls) -{ - unsigned int sent_rds; - t = NULL; - qe = GNUNET_NAMESTORE_records_store2 (nsh, - &privkey, - TEST_RECORD_COUNT - bulk_count, - &ri[bulk_count], - &sent_rds, - &put_cont_bulk_tx, - NULL); - bulk_count += sent_rds; - GNUNET_assert (sent_rds != 0); -} - - -static void -begin_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - unsigned int sent_rds; - qe = GNUNET_NAMESTORE_records_store2 (nsh, - &privkey, - TEST_RECORD_COUNT - bulk_count, - &ri[bulk_count], - &sent_rds, - &put_cont_bulk_tx, - NULL); - bulk_count += sent_rds; - GNUNET_assert (sent_rds != 0); -} - -static void -publish_records_bulk (void *cls); - -static void -put_cont_bulk (void *cls, - enum GNUNET_ErrorCode ec) -{ - struct GNUNET_TIME_Relative delay; - - (void) cls; - qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (bulk_count == TEST_RECORD_COUNT) - { - delay = GNUNET_TIME_absolute_get_duration (start); - fprintf (stdout, - "BULK: Publishing %u records took %s\n", - TEST_RECORD_COUNT, - GNUNET_STRINGS_relative_time_to_string (delay, - GNUNET_YES)); - start = GNUNET_TIME_absolute_get (); - bulk_count = 0; - qe = GNUNET_NAMESTORE_transaction_begin (nsh, begin_cont, NULL); - return; - } - (void) cls; - qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - t = GNUNET_SCHEDULER_add_now (&publish_records_bulk, NULL); -} - -static void -publish_records_bulk (void *cls) -{ - static unsigned int sent_rds = 0; - (void) cls; - t = NULL; - qe = GNUNET_NAMESTORE_records_store2 (nsh, - &privkey, - TEST_RECORD_COUNT - bulk_count, - &ri[bulk_count], - &sent_rds, - &put_cont_bulk, - NULL); - bulk_count += sent_rds; - GNUNET_assert (sent_rds != 0); -} - - -static void -put_cont_single (void *cls, - enum GNUNET_ErrorCode ec) -{ - struct GNUNET_TIME_Relative delay; - (void) cls; - qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - single_put_pos++; - if (single_put_pos == TEST_RECORD_COUNT) - { - delay = GNUNET_TIME_absolute_get_duration (start); - fprintf (stdout, - "SINGLE: Publishing %u records took %s\n", - TEST_RECORD_COUNT, - GNUNET_STRINGS_relative_time_to_string (delay, - GNUNET_YES)); - start = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_now (&publish_records_bulk, NULL); - return; - } - t = GNUNET_SCHEDULER_add_now (&publish_records_single, - NULL); -} - - -static void -publish_records_single (void *cls) -{ - struct GNUNET_TIME_Relative delay; - - (void) cls; - t = NULL; - if (single_put_pos == TEST_RECORD_COUNT) - { - delay = GNUNET_TIME_absolute_get_duration (start); - fprintf (stdout, - "Publishing %u records took %s\n", - TEST_RECORD_COUNT, - GNUNET_STRINGS_relative_time_to_string (delay, - GNUNET_YES)); - GNUNET_SCHEDULER_add_now (&publish_records_bulk, NULL); - } - qe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - ri[single_put_pos].a_label, - ri[single_put_pos].a_rd_count, - ri[single_put_pos].a_rd, - &put_cont_single, - NULL); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - - for (int i = 0; i < TEST_RECORD_COUNT; i++) - { - ri[i].a_rd = create_record (1); - ri[i].a_rd_count = 1; - GNUNET_asprintf ((char**) &ri[i].a_label, "label_%d", i); - } - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &timeout, - NULL); - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_assert (NULL != nsh); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - start = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_now (&publish_records_single, - NULL); -} - - -#include "test_common.c" - - -int -main (int argc, - char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG2 ("perf_namestore_api_%s.conf", plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("perf-namestore-api-import", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of perf_namestore_api_zone_iteration.c */ diff --git a/src/namestore/perf_namestore_api_postgres.conf b/src/namestore/perf_namestore_api_postgres.conf deleted file mode 100644 index 5e02c2df3..000000000 --- a/src/namestore/perf_namestore_api_postgres.conf +++ /dev/null @@ -1,12 +0,0 @@ -@INLINE@ test_namestore_api_postgres.conf - -[namestore] -DATABASE = postgres - -[namecache] -DISABLE = YES - -[namestore-postgres] -CONFIG = connect_timeout=10 dbname=gnunetcheck -#TEMPORARY_TABLE = YES -INIT_ON_CONNECT = YES diff --git a/src/namestore/perf_namestore_api_sqlite.conf b/src/namestore/perf_namestore_api_sqlite.conf deleted file mode 100644 index 55c3dc812..000000000 --- a/src/namestore/perf_namestore_api_sqlite.conf +++ /dev/null @@ -1,8 +0,0 @@ -@INLINE@ test_namestore_api.conf - -[namecache] -DISABLE = YES - -[namestore-sqlite] -FILENAME = $GNUNET_TEST_HOME/namestore/sqlite_test.db -INIT_ON_CONNECT = YES diff --git a/src/namestore/perf_namestore_api_zone_iteration.c b/src/namestore/perf_namestore_api_zone_iteration.c deleted file mode 100644 index e16748f5b..000000000 --- a/src/namestore/perf_namestore_api_zone_iteration.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/perf_namestore_api_zone_iteration.c - * @brief testcase for zone iteration functionality: iterate all zones - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -/** - * A #BENCHMARK_SIZE of 1000 takes less than a minute on a reasonably - * modern system, so 30 minutes should be OK even for very, very - * slow systems. - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30) - -/** - * The runtime of the benchmark is expected to be linear - * for the iteration phase with a *good* database. The FLAT - * database uses a quadratic retrieval algorithm, - * hence it should be quadratic in the size. - */ -#define BENCHMARK_SIZE 1000 - -/** - * Maximum record size - */ -#define MAX_REC_SIZE 500 - -/** - * How big are the blocks we fetch? Note that the first block is - * always just 1 record set per current API. Smaller block - * sizes will make quadratic iteration-by-offset penalties - * more pronounced. - */ -#define BLOCK_SIZE 100 - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *timeout_task; - -static struct GNUNET_SCHEDULER_Task *t; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_NAMESTORE_ZoneIterator *zi; - -static struct GNUNET_NAMESTORE_QueueEntry *qe; - -static int res; - -static unsigned int off; - -static unsigned int left_until_next; - -static uint8_t seen[1 + BENCHMARK_SIZE / 8]; - -static struct GNUNET_TIME_Absolute start; - - -/** - * Terminate everything - * - * @param cls NULL - */ -static void -end (void *cls) -{ - (void) cls; - if (NULL != qe) - { - GNUNET_NAMESTORE_cancel (qe); - qe = NULL; - } - if (NULL != zi) - { - GNUNET_NAMESTORE_zone_iteration_stop (zi); - zi = NULL; - } - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - if (NULL != t) - { - GNUNET_SCHEDULER_cancel (t); - t = NULL; - } - if (NULL != timeout_task) - { - GNUNET_SCHEDULER_cancel (timeout_task); - timeout_task = NULL; - } -} - - -/** - * End with timeout. As this is a benchmark, we do not - * fail hard but return "skipped". - */ -static void -timeout (void *cls) -{ - (void) cls; - timeout_task = NULL; - GNUNET_SCHEDULER_shutdown (); - res = 77; -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_malloc (count + sizeof(struct GNUNET_GNSRECORD_Data)); - rd->expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd->record_type = TEST_RECORD_TYPE; - rd->data_size = count; - rd->data = (void *) &rd[1]; - rd->flags = 0; - memset (&rd[1], - 'a', - count); - return rd; -} - - -static void -zone_end (void *cls) -{ - struct GNUNET_TIME_Relative delay; - - zi = NULL; - delay = GNUNET_TIME_absolute_get_duration (start); - fprintf (stdout, - "Iterating over %u records took %s\n", - off, - GNUNET_STRINGS_relative_time_to_string (delay, - GNUNET_YES)); - if (BENCHMARK_SIZE == off) - { - res = 0; - } - else - { - GNUNET_break (0); - res = 1; - } - GNUNET_SCHEDULER_shutdown (); -} - - -static void -fail_cb (void *cls) -{ - zi = NULL; - res = 2; - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct GNUNET_GNSRECORD_Data *wrd; - unsigned int xoff; - - GNUNET_assert (NULL != zone); - if (1 != sscanf (label, - "l%u", - &xoff)) - { - res = 3; - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - if ((xoff > BENCHMARK_SIZE) || - (0 != (seen[xoff / 8] & (1U << (xoff % 8))))) - { - res = 3; - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - seen[xoff / 8] |= (1U << (xoff % 8)); - wrd = create_record (xoff % MAX_REC_SIZE); - if ((rd->record_type != wrd->record_type) || - (rd->data_size != wrd->data_size) || - (rd->flags != wrd->flags)) - { - res = 4; - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (wrd); - return; - } - if (0 != memcmp (rd->data, - wrd->data, - wrd->data_size)) - { - res = 4; - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - GNUNET_free (wrd); - return; - } - GNUNET_free (wrd); - if (0 != GNUNET_memcmp (zone, - &privkey)) - { - res = 5; - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - off++; - left_until_next--; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Obtained record %u, expecting %u more until asking for more explicitly\n", - off, - left_until_next); - if (0 == left_until_next) - { - left_until_next = BLOCK_SIZE; - GNUNET_NAMESTORE_zone_iterator_next (zi, - left_until_next); - } -} - - -static void -publish_record (void *cls); - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - (void) cls; - qe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - t = GNUNET_SCHEDULER_add_now (&publish_record, - NULL); -} - - -static void -publish_record (void *cls) -{ - struct GNUNET_GNSRECORD_Data *rd; - char *label; - - (void) cls; - t = NULL; - if (BENCHMARK_SIZE == off) - { - struct GNUNET_TIME_Relative delay; - - delay = GNUNET_TIME_absolute_get_duration (start); - fprintf (stdout, - "Inserting %u records took %s\n", - off, - GNUNET_STRINGS_relative_time_to_string (delay, - GNUNET_YES)); - start = GNUNET_TIME_absolute_get (); - off = 0; - left_until_next = 1; - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &zone_proc, - NULL, - &zone_end, - NULL); - GNUNET_assert (NULL != zi); - return; - } - rd = create_record ((++off) % MAX_REC_SIZE); - GNUNET_asprintf (&label, - "l%u", - off); - qe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - label, - 1, rd, - &put_cont, - NULL); - GNUNET_free (label); - GNUNET_free (rd); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &timeout, - NULL); - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_assert (NULL != nsh); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - start = GNUNET_TIME_absolute_get (); - t = GNUNET_SCHEDULER_add_now (&publish_record, - NULL); -} - - -#include "test_common.c" - - -int -main (int argc, - char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("perf-namestore-api-zone-iteration", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of perf_namestore_api_zone_iteration.c */ diff --git a/src/namestore/plugin_namestore_flat.c b/src/namestore/plugin_namestore_flat.c deleted file mode 100644 index 8b574d2cf..000000000 --- a/src/namestore/plugin_namestore_flat.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * This file is part of GNUnet - * Copyright (C) 2009-2015, 2018, 2019 GNUnet e.V. - * - * GNUnet is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * GNUnet is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/plugin_namestore_flat.c - * @brief file-based namestore backend - * @author Martin Schanzenbach - * @author Christian Grothoff - */ - -#include "platform.h" -#include "gnunet_namestore_plugin.h" -#include "gnunet_namestore_service.h" -#include "gnunet_gnsrecord_lib.h" -#include "namestore.h" - -/** - * Context for all functions in this plugin. - */ -struct Plugin -{ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Database filename. - */ - char *fn; - - /** - * HashMap - */ - struct GNUNET_CONTAINER_MultiHashMap *hm; -}; - - -struct FlatFileEntry -{ - /** - * Entry zone - */ - struct GNUNET_CRYPTO_PrivateKey private_key; - - /** - * Record count. - */ - uint32_t record_count; - - /** - * Rvalue - */ - uint64_t rvalue; - - /** - * Record data - */ - struct GNUNET_GNSRECORD_Data *record_data; - - /** - * Label - */ - char *label; -}; - - -/** - * Hash contactenation of @a pkey and @a label into @a h - * - * @param pkey a key - * @param label a label - * @param[out] h initialized hash - */ -static void -hash_pkey_and_label (const struct GNUNET_CRYPTO_PrivateKey *pkey, - const char *label, - struct GNUNET_HashCode *h) -{ - char *key; - size_t label_len; - size_t key_len; - - label_len = strlen (label); - key_len = label_len + sizeof(struct GNUNET_CRYPTO_PrivateKey); - key = GNUNET_malloc (key_len); - GNUNET_memcpy (key, - label, - label_len); - GNUNET_memcpy (key + label_len, - pkey, - sizeof(struct GNUNET_CRYPTO_PrivateKey)); - GNUNET_CRYPTO_hash (key, - key_len, - h); - GNUNET_free (key); -} - - -/** - * Initialize the database connections and associated - * data structures (create tables and indices - * as needed as well). - * - * @param plugin the plugin context (state for this module) - * @return #GNUNET_OK on success - */ -static int -database_setup (struct Plugin *plugin) -{ - char *flatdbfile; - char *record_data; - char *zone_private_key; - char *record_data_b64; - char *buffer; - char *line; - char *label; - char *rvalue; - char *record_count; - size_t record_data_size; - uint64_t size; - struct GNUNET_HashCode hkey; - struct GNUNET_DISK_FileHandle *fh; - struct FlatFileEntry *entry; - struct GNUNET_DISK_MapHandle *mh; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, - "namestore-flat", - "FILENAME", - &flatdbfile)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "namestore-flat", - "FILENAME"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_DISK_file_test (flatdbfile)) - { - if (GNUNET_OK != - GNUNET_DISK_directory_create_for_file (flatdbfile)) - { - GNUNET_break (0); - GNUNET_free (flatdbfile); - return GNUNET_SYSERR; - } - } - /* flatdbfile should be UTF-8-encoded. If it isn't, it's a bug */ - plugin->fn = flatdbfile; - - /* Load data from file into hashmap */ - plugin->hm = GNUNET_CONTAINER_multihashmap_create (10, - GNUNET_NO); - fh = GNUNET_DISK_file_open (flatdbfile, - GNUNET_DISK_OPEN_CREATE - | GNUNET_DISK_OPEN_READWRITE, - GNUNET_DISK_PERM_USER_WRITE - | GNUNET_DISK_PERM_USER_READ); - if (NULL == fh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to initialize file: %s.\n"), - flatdbfile); - return GNUNET_SYSERR; - } - if (GNUNET_SYSERR == - GNUNET_DISK_file_size (flatdbfile, - &size, - GNUNET_YES, - GNUNET_YES)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to get filesize: %s.\n"), - flatdbfile); - GNUNET_DISK_file_close (fh); - return GNUNET_SYSERR; - } - if (size > SIZE_MAX) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("File too big to map: %llu bytes.\n"), - (unsigned long long) size); - GNUNET_DISK_file_close (fh); - return GNUNET_SYSERR; - } - if (0 == size) - { - GNUNET_DISK_file_close (fh); - return GNUNET_OK; - } - buffer = GNUNET_DISK_file_map (fh, - &mh, - GNUNET_DISK_MAP_TYPE_READ, - size); - if (NULL == buffer) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "mmap"); - GNUNET_DISK_file_close (fh); - return GNUNET_SYSERR; - } - if ('\0' != buffer[size - 1]) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore database file `%s' malformed\n"), - flatdbfile); - GNUNET_DISK_file_unmap (mh); - GNUNET_DISK_file_close (fh); - return GNUNET_SYSERR; - } - - line = strtok (buffer, "\n"); - while (NULL != line) - { - zone_private_key = strtok (line, ","); - if (NULL == zone_private_key) - break; - rvalue = strtok (NULL, ","); - if (NULL == rvalue) - break; - record_count = strtok (NULL, ","); - if (NULL == record_count) - break; - record_data_b64 = strtok (NULL, ","); - if (NULL == record_data_b64) - break; - label = strtok (NULL, ","); - if (NULL == label) - break; - line = strtok (NULL, "\n"); - entry = GNUNET_new (struct FlatFileEntry); - { - unsigned long long ll; - - if (1 != sscanf (rvalue, - "%llu", - &ll)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error parsing entry\n"); - GNUNET_free (entry); - break; - } - entry->rvalue = (uint64_t) ll; - } - { - unsigned int ui; - - if (1 != sscanf (record_count, - "%u", - &ui)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error parsing entry\n"); - GNUNET_free (entry); - break; - } - entry->record_count = (uint32_t) ui; - } - entry->label = GNUNET_strdup (label); - record_data_size - = GNUNET_STRINGS_base64_decode (record_data_b64, - strlen (record_data_b64), - (void **) &record_data); - entry->record_data = - GNUNET_new_array (entry->record_count, - struct GNUNET_GNSRECORD_Data); - if (GNUNET_OK != - GNUNET_GNSRECORD_records_deserialize (record_data_size, - record_data, - entry->record_count, - entry->record_data)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to deserialize record %s\n", - label); - GNUNET_free (entry->label); - GNUNET_free (entry); - GNUNET_free (record_data); - break; - } - GNUNET_free (record_data); - - { - struct GNUNET_CRYPTO_PrivateKey *private_key; - - GNUNET_STRINGS_base64_decode (zone_private_key, - strlen (zone_private_key), - (void **) &private_key); - entry->private_key = *private_key; - GNUNET_free (private_key); - } - - hash_pkey_and_label (&entry->private_key, - label, - &hkey); - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_put (plugin->hm, - &hkey, - entry, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_free (entry); - GNUNET_break (0); - } - } - GNUNET_DISK_file_unmap (mh); - GNUNET_DISK_file_close (fh); - return GNUNET_OK; -} - - -/** - * Store values in hashmap in file and free data - * - * @param plugin the plugin context - * @param key key in the map - * @param value a `struct FlatFileEntry` - */ -static int -store_and_free_entries (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct GNUNET_DISK_FileHandle *fh = cls; - struct FlatFileEntry *entry = value; - char *line; - char *zone_private_key; - char *record_data_b64; - ssize_t data_size; - - (void) key; - GNUNET_STRINGS_base64_encode (&entry->private_key, - sizeof(struct GNUNET_CRYPTO_PrivateKey), - &zone_private_key); - data_size = GNUNET_GNSRECORD_records_get_size (entry->record_count, - entry->record_data); - if (data_size < 0) - { - GNUNET_break (0); - GNUNET_free (zone_private_key); - return GNUNET_SYSERR; - } - if (data_size >= UINT16_MAX) - { - GNUNET_break (0); - GNUNET_free (zone_private_key); - return GNUNET_SYSERR; - } - { - char data[data_size]; - ssize_t ret; - - ret = GNUNET_GNSRECORD_records_serialize (entry->record_count, - entry->record_data, - data_size, - data); - if ((ret < 0) || - (data_size != ret)) - { - GNUNET_break (0); - GNUNET_free (zone_private_key); - return GNUNET_SYSERR; - } - GNUNET_STRINGS_base64_encode (data, - data_size, - &record_data_b64); - } - GNUNET_asprintf (&line, - "%s,%llu,%u,%s,%s\n", - zone_private_key, - (unsigned long long) entry->rvalue, - (unsigned int) entry->record_count, - record_data_b64, - entry->label); - GNUNET_free (record_data_b64); - GNUNET_free (zone_private_key); - - GNUNET_DISK_file_write (fh, - line, - strlen (line)); - - GNUNET_free (line); - GNUNET_free (entry->label); - GNUNET_free (entry->record_data); - GNUNET_free (entry); - return GNUNET_YES; -} - - -/** - * Shutdown database connection and associate data - * structures. - * @param plugin the plugin context (state for this module) - */ -static void -database_shutdown (struct Plugin *plugin) -{ - struct GNUNET_DISK_FileHandle *fh; - - fh = GNUNET_DISK_file_open (plugin->fn, - GNUNET_DISK_OPEN_CREATE - | GNUNET_DISK_OPEN_TRUNCATE - | GNUNET_DISK_OPEN_READWRITE, - GNUNET_DISK_PERM_USER_WRITE - | GNUNET_DISK_PERM_USER_READ); - if (NULL == fh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to initialize file: %s.\n"), - plugin->fn); - return; - } - - GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, - &store_and_free_entries, - fh); - GNUNET_CONTAINER_multihashmap_destroy (plugin->hm); - /* append 0-terminator */ - GNUNET_DISK_file_write (fh, - "", - 1); - GNUNET_DISK_file_close (fh); -} - - -/** - * Store a record in the datastore. Removes any existing record in the - * same zone with the same name. - * - * @param cls closure (internal context for the plugin) - * @param zone_key private key of the zone - * @param label name that is being mapped (at most 255 characters long) - * @param rd_count number of entries in @a rd array - * @param rd array of records with data to store - * @return #GNUNET_OK on success, else #GNUNET_SYSERR - */ -static int -namestore_flat_store_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone_key, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct Plugin *plugin = cls; - uint64_t rvalue; - struct GNUNET_HashCode hkey; - struct FlatFileEntry *entry; - - rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - UINT64_MAX); - hash_pkey_and_label (zone_key, - label, - &hkey); - GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, - &hkey); - if (0 == rd_count) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "sqlite", - "Record deleted\n"); - return GNUNET_OK; - } - entry = GNUNET_new (struct FlatFileEntry); - GNUNET_asprintf (&entry->label, - label, - strlen (label)); - GNUNET_memcpy (&entry->private_key, - zone_key, - sizeof(struct GNUNET_CRYPTO_PrivateKey)); - entry->rvalue = rvalue; - entry->record_count = rd_count; - entry->record_data = GNUNET_new_array (rd_count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int i = 0; i < rd_count; i++) - { - entry->record_data[i].expiration_time = rd[i].expiration_time; - entry->record_data[i].record_type = rd[i].record_type; - entry->record_data[i].flags = rd[i].flags; - entry->record_data[i].data_size = rd[i].data_size; - entry->record_data[i].data = GNUNET_malloc (rd[i].data_size); - GNUNET_memcpy ((char *) entry->record_data[i].data, - rd[i].data, - rd[i].data_size); - } - return GNUNET_CONTAINER_multihashmap_put (plugin->hm, - &hkey, - entry, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); -} - - -/** - * Lookup records in the datastore for which we are the authority. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone - * @param label name of the record in the zone - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR - */ -static int -namestore_flat_lookup_records (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - struct FlatFileEntry *entry; - struct GNUNET_HashCode hkey; - - if (NULL == zone) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - hash_pkey_and_label (zone, - label, - &hkey); - entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm, - &hkey); - - if (NULL == entry) - return GNUNET_NO; - if (NULL != iter) - iter (iter_cls, - 1, /* zero is illegal */ - &entry->private_key, - entry->label, - entry->record_count, - entry->record_data); - return GNUNET_YES; -} - - -/** - * Closure for #iterate_zones. - */ -struct IterateContext -{ - /** - * How many more records should we skip before returning results? - */ - uint64_t offset; - - /** - * How many more records should we return? - */ - uint64_t limit; - - /** - * What is the position of the current entry, counting - * starts from 1. - */ - uint64_t pos; - - /** - * Target zone. - */ - const struct GNUNET_CRYPTO_PrivateKey *zone; - - /** - * Function to call on each record. - */ - GNUNET_NAMESTORE_RecordIterator iter; - - /** - * Closure for @e iter. - */ - void *iter_cls; -}; - - -/** - * Helper function for #namestore_flat_iterate_records(). - * - * @param cls a `struct IterateContext` - * @param key unused - * @param value a `struct FlatFileEntry` - * @return #GNUNET_YES to continue the iteration - */ -static int -iterate_zones (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct IterateContext *ic = cls; - struct FlatFileEntry *entry = value; - - (void) key; - if (0 == ic->limit) - return GNUNET_NO; - if ((NULL != ic->zone) && - (0 != GNUNET_memcmp (&entry->private_key, - ic->zone))) - return GNUNET_YES; - ic->pos++; - if (ic->offset > 0) - { - ic->offset--; - return GNUNET_YES; - } - ic->iter (ic->iter_cls, - ic->pos, - (NULL == ic->zone) - ? &entry->private_key - : ic->zone, - entry->label, - entry->record_count, - entry->record_data); - ic->limit--; - if (0 == ic->limit) - return GNUNET_NO; - return GNUNET_YES; -} - - -/** - * Iterate over the results for a particular key and zone in the - * datastore. Will return at most one result to the iterator. - * - * @param cls closure (internal context for the plugin) - * @param zone hash of public key of the zone, NULL to iterate over all zones - * @param serial serial number to exclude in the list of all matching records - * @param limit maximum number of results to return to @a iter - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error - */ -static int -namestore_flat_iterate_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - uint64_t serial, - uint64_t limit, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - struct IterateContext ic; - - ic.offset = serial; - ic.pos = 0; - ic.limit = limit; - ic.iter = iter; - ic.iter_cls = iter_cls; - ic.zone = zone; - GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, - &iterate_zones, - &ic); - return (0 == ic.limit) ? GNUNET_OK : GNUNET_NO; -} - - -/** - * Closure for #zone_to_name. - */ -struct ZoneToNameContext -{ - const struct GNUNET_CRYPTO_PrivateKey *zone; - const struct GNUNET_CRYPTO_PublicKey *value_zone; - GNUNET_NAMESTORE_RecordIterator iter; - void *iter_cls; - - int result_found; -}; - - -static int -zone_to_name (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct ZoneToNameContext *ztn = cls; - struct FlatFileEntry *entry = value; - - (void) key; - if (0 != GNUNET_memcmp (&entry->private_key, - ztn->zone)) - return GNUNET_YES; - - for (unsigned int i = 0; i < entry->record_count; i++) - { - if (GNUNET_NO == - GNUNET_GNSRECORD_is_zonekey_type (entry->record_data[i].record_type)) - continue; - if (ztn->value_zone->type != entry->record_data[i].record_type) - continue; - if (0 == memcmp (ztn->value_zone, - entry->record_data[i].data, - entry->record_data[i].data_size)) - { - ztn->iter (ztn->iter_cls, - i + 1, /* zero is illegal! */ - &entry->private_key, - entry->label, - entry->record_count, - entry->record_data); - ztn->result_found = GNUNET_YES; - } - } - return GNUNET_YES; -} - - -/** - * Look for an existing PKEY delegation record for a given public key. - * Returns at most one result to the iterator. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone to look up in, never NULL - * @param value_zone public key of the target zone (value), never NULL - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error - */ -static int -namestore_flat_zone_to_name (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const struct - GNUNET_CRYPTO_PublicKey *value_zone, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - struct ZoneToNameContext ztn = { - .iter = iter, - .iter_cls = iter_cls, - .zone = zone, - .value_zone = value_zone, - .result_found = GNUNET_NO - }; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Performing reverse lookup for `%s'\n", - GNUNET_GNSRECORD_z2s (value_zone)); - GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, - &zone_to_name, - &ztn); - return ztn.result_found; -} - - -/** - * Entry point for the plugin. - * - * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*" - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_namestore_flat_init (void *cls) -{ - static struct Plugin plugin; - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct GNUNET_NAMESTORE_PluginFunctions *api; - - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, - 0, - sizeof(struct Plugin)); - plugin.cfg = cfg; - if (GNUNET_OK != database_setup (&plugin)) - { - database_shutdown (&plugin); - return NULL; - } - api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions); - api->cls = &plugin; - api->store_records = &namestore_flat_store_records; - api->iterate_records = &namestore_flat_iterate_records; - api->zone_to_name = &namestore_flat_zone_to_name; - api->lookup_records = &namestore_flat_lookup_records; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ ("Flat file database running\n")); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_namestore_flat_done (void *cls) -{ - struct GNUNET_NAMESTORE_PluginFunctions *api = cls; - struct Plugin *plugin = api->cls; - - database_shutdown (plugin); - plugin->cfg = NULL; - GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Flat file plugin is finished\n"); - return NULL; -} - - -/* end of plugin_namestore_flat.c */ diff --git a/src/namestore/plugin_namestore_postgres.c b/src/namestore/plugin_namestore_postgres.c deleted file mode 100644 index c6debf3a9..000000000 --- a/src/namestore/plugin_namestore_postgres.c +++ /dev/null @@ -1,796 +0,0 @@ -/* - * This file is part of GNUnet - * Copyright (C) 2009-2013, 2016-2018 GNUnet e.V. - * - * GNUnet is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * GNUnet is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file namestore/plugin_namestore_postgres.c - * @brief postgres-based namestore backend - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_namestore_plugin.h" -#include "gnunet_namestore_service.h" -#include "gnunet_gnsrecord_lib.h" -#include "gnunet_pq_lib.h" -#include "namestore.h" - - -#define LOG(kind, ...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__) - - -/** - * Context for all functions in this plugin. - */ -struct Plugin -{ - /** - * Our configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Postgres database handle. - */ - struct GNUNET_PQ_Context *dbh; - - /** - * Database is prepared and ready - */ - bool ready; -}; - - -/** - * Initialize the database connections and associated data structures (create - * tables and indices as needed as well). - * - * @param cls the plugin context (state for this module) - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_create_tables (void *cls) -{ - struct Plugin *plugin = cls; - struct GNUNET_PQ_Context *dbh; - - dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, - "namestore-postgres", - "namestore-", - NULL, - NULL); - if (NULL == dbh) - return GNUNET_SYSERR; - GNUNET_PQ_disconnect (dbh); - return GNUNET_OK; -} - - -/** - * Drop existing namestore tables. - * - * @param cls the plugin context (state for this module) - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_drop_tables (void *cls) -{ - struct Plugin *plugin = cls; - struct GNUNET_PQ_Context *dbh; - enum GNUNET_GenericReturnValue ret; - - dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, - "namestore-postgres", - NULL, - NULL, - NULL); - if (NULL == dbh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to connect to database\n"); - return GNUNET_SYSERR; - } - ret = GNUNET_PQ_exec_sql (dbh, - "namestore-drop"); - GNUNET_PQ_disconnect (dbh); - return ret; -} - - -static enum GNUNET_GenericReturnValue -database_prepare (struct Plugin *plugin) -{ - enum GNUNET_GenericReturnValue ret; - - if (plugin->ready) - return GNUNET_OK; - { - struct GNUNET_PQ_PreparedStatement ps[] = { - GNUNET_PQ_make_prepare ("store_records", - "INSERT INTO namestore.ns098records" - " (zone_private_key, pkey, rvalue, record_count, record_data, label)" - " VALUES ($1, $2, $3, $4, $5, $6)" - " ON CONFLICT ON CONSTRAINT zl" - " DO UPDATE" - " SET pkey=$2,rvalue=$3,record_count=$4,record_data=$5" - " WHERE ns098records.zone_private_key = $1" - " AND ns098records.label = $6"), - GNUNET_PQ_make_prepare ("delete_records", - "DELETE FROM namestore.ns098records " - "WHERE zone_private_key=$1 AND label=$2"), - GNUNET_PQ_make_prepare ("zone_to_name", - "SELECT seq,record_count,record_data,label FROM namestore.ns098records" - " WHERE zone_private_key=$1 AND pkey=$2"), - GNUNET_PQ_make_prepare ("iterate_zone", - "SELECT seq,record_count,record_data,label FROM namestore.ns098records " - "WHERE zone_private_key=$1 AND seq > $2 ORDER BY seq ASC LIMIT $3"), - GNUNET_PQ_make_prepare ("iterate_all_zones", - "SELECT seq,record_count,record_data,label,zone_private_key" - " FROM namestore.ns098records WHERE seq > $1 ORDER BY seq ASC LIMIT $2"), - GNUNET_PQ_make_prepare ("lookup_label", - "SELECT seq,record_count,record_data,label " - "FROM namestore.ns098records WHERE zone_private_key=$1 AND label=$2"), - GNUNET_PQ_make_prepare ("edit_set", - "SELECT seq,record_count,record_data,label " - "FROM namestore.ns098records WHERE zone_private_key=$1 AND label=$2 FOR UPDATE NOWAIT"), - GNUNET_PQ_PREPARED_STATEMENT_END - }; - - ret = GNUNET_PQ_prepare_statements (plugin->dbh, - ps); - } - if (GNUNET_OK != ret) - return ret; - plugin->ready = true; - return GNUNET_OK; -} - - -/** - * Initialize the database connections and associated - * data structures (create tables and indices - * as needed as well). - * - * @param plugin the plugin context (state for this module) - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -database_connect (struct Plugin *plugin) -{ - struct GNUNET_PQ_ExecuteStatement ess[] = { - GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - struct GNUNET_PQ_ExecuteStatement *es; - - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, - "namestore-postgres", - "ASYNC_COMMIT")) - es = &ess[0]; - else - es = &ess[1]; - - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, - "namestore-postgres", - "INIT_ON_CONNECT")) - { - if (GNUNET_OK != - namestore_postgres_create_tables (plugin)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create tables\n"); - return GNUNET_SYSERR; - } - } - plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, - "namestore-postgres", - NULL, - es, - NULL); - if (NULL == plugin->dbh) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * Store a record in the datastore. Removes any existing record in the - * same zone with the same name. - * - * @param cls closure (internal context for the plugin) - * @param zone_key private key of the zone - * @param label name that is being mapped (at most 255 characters long) - * @param rd_count number of entries in @a rd array - * @param rd array of records with data to store - * @return #GNUNET_OK on success, else #GNUNET_SYSERR - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_store_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone_key, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct Plugin *plugin = cls; - struct GNUNET_CRYPTO_PublicKey pkey; - uint64_t rvalue; - uint32_t rd_count32 = (uint32_t) rd_count; - ssize_t data_size; - - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - memset (&pkey, - 0, - sizeof(pkey)); - for (unsigned int i = 0; i < rd_count; i++) - if (GNUNET_YES == - GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type)) - { - GNUNET_break (GNUNET_OK == - GNUNET_GNSRECORD_identity_from_data (rd[i].data, - rd[i].data_size, - rd[i].record_type, - &pkey)); - break; - } - rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - UINT64_MAX); - data_size = GNUNET_GNSRECORD_records_get_size (rd_count, - rd); - if (data_size < 0) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (data_size >= UINT16_MAX) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* if record set is empty, delete existing records */ - if (0 == rd_count) - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (zone_key), - GNUNET_PQ_query_param_string (label), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus res; - - res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh, - "delete_records", - params); - if ((GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != res) && - (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != res)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "postgres", - "Record deleted\n"); - return GNUNET_OK; - } - /* otherwise, UPSERT (i.e. UPDATE if exists, otherwise INSERT) */ - { - char data[data_size]; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (zone_key), - GNUNET_PQ_query_param_auto_from_type (&pkey), - GNUNET_PQ_query_param_uint64 (&rvalue), - GNUNET_PQ_query_param_uint32 (&rd_count32), - GNUNET_PQ_query_param_fixed_size (data, data_size), - GNUNET_PQ_query_param_string (label), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus res; - ssize_t ret; - - ret = GNUNET_GNSRECORD_records_serialize (rd_count, - rd, - data_size, - data); - if ((ret < 0) || - (data_size != ret)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh, - "store_records", - params); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != res) - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Closure for #parse_result_call_iterator. - */ -struct ParserContext -{ - /** - * Function to call for each result. - */ - GNUNET_NAMESTORE_RecordIterator iter; - - /** - * Closure for @e iter. - */ - void *iter_cls; - - /** - * Zone key, NULL if part of record. - */ - const struct GNUNET_CRYPTO_PrivateKey *zone_key; - - /** - * Number of results still to return (counted down by - * number of results given to iterator). - */ - uint64_t limit; -}; - - -/** - * A statement has been run. We should evaluate the result, and if possible - * call the @a iter in @a cls with the result. - * - * @param cls closure of type `struct ParserContext *` - * @param res the postgres result - * @param num_results the number of results in @a result - */ -static void -parse_result_call_iterator (void *cls, - PGresult *res, - unsigned int num_results) -{ - struct ParserContext *pc = cls; - - if (NULL == pc->iter) - return; /* no need to do more work */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got %d results from PQ.\n", num_results); - for (unsigned int i = 0; i < num_results; i++) - { - uint64_t serial; - void *data; - size_t data_size; - uint32_t record_count; - char *label; - struct GNUNET_CRYPTO_PrivateKey zk; - struct GNUNET_PQ_ResultSpec rs_with_zone[] = { - GNUNET_PQ_result_spec_uint64 ("seq", &serial), - GNUNET_PQ_result_spec_uint32 ("record_count", &record_count), - GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size), - GNUNET_PQ_result_spec_string ("label", &label), - GNUNET_PQ_result_spec_auto_from_type ("zone_private_key", &zk), - GNUNET_PQ_result_spec_end - }; - struct GNUNET_PQ_ResultSpec rs_without_zone[] = { - GNUNET_PQ_result_spec_uint64 ("seq", &serial), - GNUNET_PQ_result_spec_uint32 ("record_count", &record_count), - GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size), - GNUNET_PQ_result_spec_string ("label", &label), - GNUNET_PQ_result_spec_end - }; - struct GNUNET_PQ_ResultSpec *rs; - - rs = (NULL == pc->zone_key) ? rs_with_zone : rs_without_zone; - if (GNUNET_YES != - GNUNET_PQ_extract_result (res, - rs, - i)) - { - GNUNET_break (0); - return; - } - - if (record_count > 64 * 1024) - { - /* sanity check, don't stack allocate far too much just - because database might contain a large value here */ - GNUNET_break (0); - GNUNET_PQ_cleanup_result (rs); - return; - } - - { - struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (record_count)]; - - GNUNET_assert (0 != serial); - if (GNUNET_OK != - GNUNET_GNSRECORD_records_deserialize (data_size, - data, - record_count, - rd)) - { - GNUNET_break (0); - GNUNET_PQ_cleanup_result (rs); - return; - } - pc->iter (pc->iter_cls, - serial, - (NULL == pc->zone_key) ? &zk : pc->zone_key, - label, - record_count, - rd); - } - GNUNET_PQ_cleanup_result (rs); - } - pc->limit -= num_results; -} - - -/** - * Lookup records in the datastore for which we are the authority. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone - * @param label name of the record in the zone - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @param method the method to use "lookup_record" or "edit_set" - * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR - */ -static enum GNUNET_GenericReturnValue -lookup_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls, - const char*method) -{ - struct Plugin *plugin = cls; - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (zone), - GNUNET_PQ_query_param_string (label), - GNUNET_PQ_query_param_end - }; - struct ParserContext pc; - enum GNUNET_DB_QueryStatus res; - - if (NULL == zone) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - pc.iter = iter; - pc.iter_cls = iter_cls; - pc.zone_key = zone; - res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, - method, - params, - &parse_result_call_iterator, - &pc); - if (res < 0) - return GNUNET_SYSERR; - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res) - return GNUNET_NO; - return GNUNET_OK; -} - - -/** - * Lookup records in the datastore for which we are the authority. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone - * @param label name of the record in the zone - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_lookup_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - return lookup_records (cls, zone, label, iter, iter_cls, "lookup_label"); -} - - -/** - * Edit records in the datastore for which we are the authority. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone - * @param label name of the record in the zone - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR - */ -static int -namestore_postgres_edit_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - return lookup_records (cls, zone, label, iter, iter_cls, "edit_set"); -} - - -/** - * Iterate over the results for a particular key and zone in the - * datastore. Will return at most one result to the iterator. - * - * @param cls closure (internal context for the plugin) - * @param zone hash of public key of the zone, NULL to iterate over all zones - * @param serial serial number to exclude in the list of all matching records - * @param limit maximum number of results to fetch - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_iterate_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - uint64_t serial, - uint64_t limit, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - enum GNUNET_DB_QueryStatus res; - struct ParserContext pc; - - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - pc.iter = iter; - pc.iter_cls = iter_cls; - pc.zone_key = zone; - pc.limit = limit; - if (NULL == zone) - { - struct GNUNET_PQ_QueryParam params_without_zone[] = { - GNUNET_PQ_query_param_uint64 (&serial), - GNUNET_PQ_query_param_uint64 (&limit), - GNUNET_PQ_query_param_end - }; - - res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, - "iterate_all_zones", - params_without_zone, - &parse_result_call_iterator, - &pc); - } - else - { - struct GNUNET_PQ_QueryParam params_with_zone[] = { - GNUNET_PQ_query_param_auto_from_type (zone), - GNUNET_PQ_query_param_uint64 (&serial), - GNUNET_PQ_query_param_uint64 (&limit), - GNUNET_PQ_query_param_end - }; - - res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, - "iterate_zone", - params_with_zone, - &parse_result_call_iterator, - &pc); - } - if (res < 0) - return GNUNET_SYSERR; - - if ((GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res) || - (pc.limit > 0)) - return GNUNET_NO; - return GNUNET_OK; -} - - -/** - * Look for an existing PKEY delegation record for a given public key. - * Returns at most one result to the iterator. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone to look up in, never NULL - * @param value_zone public key of the target zone (value), never NULL - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_zone_to_name (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - const struct - GNUNET_CRYPTO_PublicKey *value_zone, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (zone), - GNUNET_PQ_query_param_auto_from_type (value_zone), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus res; - struct ParserContext pc; - - pc.iter = iter; - pc.iter_cls = iter_cls; - pc.zone_key = zone; - res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, - "zone_to_name", - params, - &parse_result_call_iterator, - &pc); - if (res < 0) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * Begin a transaction for a client. - * - * @param cls closure (internal context for the plugin) - * @param emsg error message set of return code is #GNUNET_SYSERR - * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_transaction_begin (void *cls, - char **emsg) -{ - struct Plugin *plugin = cls; - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("BEGIN"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - return GNUNET_PQ_exec_statements (plugin->dbh, es); -} - - -/** - * Commit a transaction for a client. - * This releases the lock on the database. - * - * @param cls closure (internal context for the plugin) - * @param emsg error message set of return code is #GNUNET_SYSERR - * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_transaction_rollback (void *cls, - char **emsg) -{ - struct Plugin *plugin = cls; - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("ROLLBACK"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - return GNUNET_PQ_exec_statements (plugin->dbh, es); -} - - -/** - * Roll back a transaction for a client. - * This releases the lock on the database. - * - * @param cls closure (internal context for the plugin) - * @param emsg error message set of return code is #GNUNET_SYSERR - * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. - */ -static enum GNUNET_GenericReturnValue -namestore_postgres_transaction_commit (void *cls, - char **emsg) -{ - struct Plugin *plugin = cls; - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("COMMIT"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - return GNUNET_PQ_exec_statements (plugin->dbh, es); -} - - -/** - * Shutdown database connection and associate data - * structures. - * - * @param plugin the plugin context (state for this module) - */ -static void -database_shutdown (struct Plugin *plugin) -{ - GNUNET_PQ_disconnect (plugin->dbh); - plugin->dbh = NULL; -} - - -/** - * Entry point for the plugin. - * - * @param cls the `struct GNUNET_NAMESTORE_PluginEnvironment*` - * @return NULL on error, othrewise the plugin context - */ -void * -libgnunet_plugin_namestore_postgres_init (void *cls) -{ - struct Plugin *plugin; - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct GNUNET_NAMESTORE_PluginFunctions *api; - - plugin = GNUNET_new (struct Plugin); - plugin->cfg = cfg; - if (GNUNET_OK != database_connect (plugin)) - { - database_shutdown (plugin); - GNUNET_free (plugin); - return NULL; - } - api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions); - api->cls = plugin; - api->create_tables = &namestore_postgres_create_tables; - api->drop_tables = &namestore_postgres_drop_tables; - api->store_records = &namestore_postgres_store_records; - api->iterate_records = &namestore_postgres_iterate_records; - api->zone_to_name = &namestore_postgres_zone_to_name; - api->lookup_records = &namestore_postgres_lookup_records; - api->transaction_begin = &namestore_postgres_transaction_begin; - api->transaction_commit = &namestore_postgres_transaction_commit; - api->transaction_rollback = &namestore_postgres_transaction_rollback; - api->edit_records = &namestore_postgres_edit_records; - LOG (GNUNET_ERROR_TYPE_INFO, - "Postgres namestore plugin running\n"); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_namestore_postgres_done (void *cls) -{ - struct GNUNET_NAMESTORE_PluginFunctions *api = cls; - struct Plugin *plugin = api->cls; - - database_shutdown (plugin); - plugin->cfg = NULL; - GNUNET_free (plugin); - GNUNET_free (api); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Postgres namestore plugin is finished\n"); - return NULL; -} - - -/* end of plugin_namestore_postgres.c */ diff --git a/src/namestore/plugin_namestore_sqlite.c b/src/namestore/plugin_namestore_sqlite.c deleted file mode 100644 index d66271ffa..000000000 --- a/src/namestore/plugin_namestore_sqlite.c +++ /dev/null @@ -1,983 +0,0 @@ -/* - * This file is part of GNUnet - * Copyright (C) 2009-2017 GNUnet e.V. - * - * GNUnet is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, - * or (at your option) any later version. - * - * GNUnet is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file namestore/plugin_namestore_sqlite.c - * @brief sqlite-based namestore backend - * @author Christian Grothoff - */ - -#include "platform.h" -#include "gnunet_namestore_plugin.h" -#include "gnunet_namestore_service.h" -#include "gnunet_gnsrecord_lib.h" -#include "gnunet_sq_lib.h" -#include "namestore.h" -#include - -/** - * After how many ms "busy" should a DB operation fail for good? A - * low value makes sure that we are more responsive to requests - * (especially PUTs). A high value guarantees a higher success rate - * (SELECTs in iterate can take several seconds despite LIMIT=1). - * - * The default value of 1s should ensure that users do not experience - * huge latencies while at the same time allowing operations to - * succeed with reasonable probability. - */ -#define BUSY_TIMEOUT_MS 1000 - - -/** - * Log an error message at log-level 'level' that indicates - * a failure of the command 'cmd' on file 'filename' - * with the message given by strerror(errno). - */ -#define LOG_SQLITE(db, level, cmd) do { \ - GNUNET_log_from (level, \ - "namestore-sqlite", _ ( \ - "`%s' failed at %s:%d with error: %s\n"), \ - cmd, \ - __FILE__, __LINE__, \ - sqlite3_errmsg ( \ - db->dbh)); \ -} while (0) - -#define LOG(kind, ...) GNUNET_log_from (kind, "namestore-sqlite", __VA_ARGS__) - - -/** - * Context for all functions in this plugin. - */ -struct Plugin -{ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Database filename. - */ - char *fn; - - /** - * Statements prepared, we are ready to go if true. - */ - bool ready; - - /** - * Native SQLite database handle. - */ - sqlite3 *dbh; - - /** - * Precompiled SQL to store records. - */ - sqlite3_stmt *store_records; - - /** - * Precompiled SQL to deltete existing records. - */ - sqlite3_stmt *delete_records; - - /** - * Precompiled SQL for iterate records within a zone. - */ - sqlite3_stmt *iterate_zone; - - /** - * Precompiled SQL for iterate all records within all zones. - */ - sqlite3_stmt *iterate_all_zones; - - /** - * Precompiled SQL to for reverse lookup based on PKEY. - */ - sqlite3_stmt *zone_to_name; - - /** - * Precompiled SQL to lookup records based on label. - */ - sqlite3_stmt *lookup_label; -}; - - -/** - * Initialize the database connections and associated - * data structures (create tables and indices - * as needed as well). - * - * @param plugin the plugin context (state for this module) - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -database_prepare (struct Plugin *plugin) -{ - if (plugin->ready) - return GNUNET_OK; - struct GNUNET_SQ_ExecuteStatement es[] = { - GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"), - GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"), - GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"), - GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"), - GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""), - GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=NORMAL"), - GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"), - GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"), - GNUNET_SQ_EXECUTE_STATEMENT_END - }; - struct GNUNET_SQ_PrepareStatement ps[] = { - GNUNET_SQ_make_prepare ("INSERT INTO ns098records " - "(zone_private_key,pkey,rvalue,record_count,record_data,label)" - " VALUES (?, ?, ?, ?, ?, ?)", - &plugin->store_records), - GNUNET_SQ_make_prepare ("DELETE FROM ns098records " - "WHERE zone_private_key=? AND label=?", - &plugin->delete_records), - GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label" - " FROM ns098records" - " WHERE zone_private_key=? AND pkey=?", - &plugin->zone_to_name), - GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label" - " FROM ns098records" - " WHERE zone_private_key=? AND uid > ?" - " ORDER BY uid ASC" - " LIMIT ?", - &plugin->iterate_zone), - GNUNET_SQ_make_prepare ( - "SELECT uid,record_count,record_data,label,zone_private_key" - " FROM ns098records" - " WHERE uid > ?" - " ORDER BY uid ASC" - " LIMIT ?", - &plugin->iterate_all_zones), - GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label" - " FROM ns098records" - " WHERE zone_private_key=? AND label=?", - &plugin->lookup_label), - GNUNET_SQ_PREPARE_END - }; - - if (GNUNET_OK != - GNUNET_SQ_exec_statements (plugin->dbh, - es)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Failed to setup database with: `%s'\n"), - sqlite3_errmsg (plugin->dbh)); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_SQ_prepare (plugin->dbh, - ps)) - { - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Failed to setup database with: `%s'\n"), - sqlite3_errmsg (plugin->dbh)); - return GNUNET_SYSERR; - } - plugin->ready = GNUNET_YES; - return GNUNET_OK; -} - - -/** - * Shutdown database connection and associate data - * structures. - * @param plugin the plugin context (state for this module) - */ -static void -database_shutdown (struct Plugin *plugin) -{ - int result; - sqlite3_stmt *stmt; - - if (NULL != plugin->store_records) - sqlite3_finalize (plugin->store_records); - if (NULL != plugin->delete_records) - sqlite3_finalize (plugin->delete_records); - if (NULL != plugin->iterate_zone) - sqlite3_finalize (plugin->iterate_zone); - if (NULL != plugin->iterate_all_zones) - sqlite3_finalize (plugin->iterate_all_zones); - if (NULL != plugin->zone_to_name) - sqlite3_finalize (plugin->zone_to_name); - if (NULL != plugin->lookup_label) - sqlite3_finalize (plugin->lookup_label); - result = sqlite3_close (plugin->dbh); - if (result == SQLITE_BUSY) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _ ( - "Tried to close sqlite without finalizing all prepared statements.\n")); - stmt = sqlite3_next_stmt (plugin->dbh, - NULL); - while (NULL != stmt) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "sqlite", - "Closing statement %p\n", - stmt); - result = sqlite3_finalize (stmt); - if (result != SQLITE_OK) - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, - "sqlite", - "Failed to close statement %p: %d\n", - stmt, - result); - stmt = sqlite3_next_stmt (plugin->dbh, - NULL); - } - result = sqlite3_close (plugin->dbh); - } - if (SQLITE_OK != result) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR, - "sqlite3_close"); - -} - - -/** - * Store a record in the datastore. Removes any existing record in the - * same zone with the same name. - * - * @param cls closure (internal context for the plugin) - * @param zone_key private key of the zone - * @param label name that is being mapped (at most 255 characters long) - * @param rd_count number of entries in @a rd array - * @param rd array of records with data to store - * @return #GNUNET_OK on success, else #GNUNET_SYSERR - */ -static enum GNUNET_GenericReturnValue -namestore_sqlite_store_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone_key, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct Plugin *plugin = cls; - int n; - struct GNUNET_CRYPTO_PublicKey pkey; - uint64_t rvalue; - ssize_t data_size; - - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - memset (&pkey, - 0, - sizeof(pkey)); - for (unsigned int i = 0; i < rd_count; i++) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Checking if `%d' is zonekey type\n", - rd[i].record_type); - - if (GNUNET_YES == GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type)) - { - GNUNET_break (GNUNET_YES == - GNUNET_GNSRECORD_identity_from_data (rd[i].data, - rd[i].data_size, - rd[i].record_type, - &pkey)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Storing delegation zone record value `%s'\n", - GNUNET_GNSRECORD_z2s (&pkey)); - - break; - } - } - rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - UINT64_MAX); - data_size = GNUNET_GNSRECORD_records_get_size (rd_count, - rd); - if (data_size < 0) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (data_size > 64 * 65536) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - { - /* First delete 'old' records */ - char data[data_size]; - struct GNUNET_SQ_QueryParam dparams[] = { - GNUNET_SQ_query_param_auto_from_type (zone_key), - GNUNET_SQ_query_param_string (label), - GNUNET_SQ_query_param_end - }; - ssize_t ret; - - ret = GNUNET_GNSRECORD_records_serialize (rd_count, - rd, - data_size, - data); - if ((ret < 0) || - (data_size != ret)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_SQ_bind (plugin->delete_records, - dparams)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind_XXXX"); - GNUNET_SQ_reset (plugin->dbh, - plugin->delete_records); - return GNUNET_SYSERR; - } - n = sqlite3_step (plugin->delete_records); - GNUNET_SQ_reset (plugin->dbh, - plugin->delete_records); - - if (0 != rd_count) - { - uint32_t rd_count32 = (uint32_t) rd_count; - struct GNUNET_SQ_QueryParam sparams[] = { - GNUNET_SQ_query_param_auto_from_type (zone_key), - GNUNET_SQ_query_param_auto_from_type (&pkey), - GNUNET_SQ_query_param_uint64 (&rvalue), - GNUNET_SQ_query_param_uint32 (&rd_count32), - GNUNET_SQ_query_param_fixed_size (data, data_size), - GNUNET_SQ_query_param_string (label), - GNUNET_SQ_query_param_end - }; - - if (GNUNET_OK != - GNUNET_SQ_bind (plugin->store_records, - sparams)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind_XXXX"); - GNUNET_SQ_reset (plugin->dbh, - plugin->store_records); - return GNUNET_SYSERR; - } - n = sqlite3_step (plugin->store_records); - GNUNET_SQ_reset (plugin->dbh, - plugin->store_records); - } - } - switch (n) - { - case SQLITE_DONE: - if (0 != rd_count) - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "sqlite", - "Record stored\n"); - else - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "sqlite", - "Record deleted\n"); - return GNUNET_OK; - - case SQLITE_BUSY: - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, - "sqlite3_step"); - return GNUNET_NO; - - default: - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_step"); - return GNUNET_SYSERR; - } -} - - -/** - * The given 'sqlite' statement has been prepared to be run. - * It will return a record which should be given to the iterator. - * Runs the statement and parses the returned record. - * - * @param plugin plugin context - * @param stmt to run (and then clean up) - * @param zone_key private key of the zone - * @param limit maximum number of results to fetch - * @param iter iterator to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error - */ -static enum GNUNET_GenericReturnValue -get_records_and_call_iterator (struct Plugin *plugin, - sqlite3_stmt *stmt, - const struct - GNUNET_CRYPTO_PrivateKey *zone_key, - uint64_t limit, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - int ret; - int sret; - - ret = GNUNET_OK; - for (uint64_t i = 0; i < limit; i++) - { - sret = sqlite3_step (stmt); - - if (SQLITE_DONE == sret) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Iteration done (no results)\n"); - ret = GNUNET_NO; - break; - } - if (SQLITE_ROW != sret) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR, - "sqlite_step"); - ret = GNUNET_SYSERR; - break; - } - - { - uint64_t seq; - uint32_t record_count; - size_t data_size; - void *data; - char *label; - struct GNUNET_CRYPTO_PrivateKey zk; - struct GNUNET_SQ_ResultSpec rs[] = { - GNUNET_SQ_result_spec_uint64 (&seq), - GNUNET_SQ_result_spec_uint32 (&record_count), - GNUNET_SQ_result_spec_variable_size (&data, - &data_size), - GNUNET_SQ_result_spec_string (&label), - GNUNET_SQ_result_spec_end - }; - struct GNUNET_SQ_ResultSpec rsx[] = { - GNUNET_SQ_result_spec_uint64 (&seq), - GNUNET_SQ_result_spec_uint32 (&record_count), - GNUNET_SQ_result_spec_variable_size (&data, - &data_size), - GNUNET_SQ_result_spec_string (&label), - GNUNET_SQ_result_spec_auto_from_type (&zk), - GNUNET_SQ_result_spec_end - }; - - ret = GNUNET_SQ_extract_result (stmt, - (NULL == zone_key) - ? rsx - : rs); - if ((GNUNET_OK != ret) || - (record_count > 64 * 1024)) - { - /* sanity check, don't stack allocate far too much just - because database might contain a large value here */ - GNUNET_break (0); - ret = GNUNET_SYSERR; - break; - } - else - { - struct GNUNET_GNSRECORD_Data rd[record_count]; - - GNUNET_assert (0 != seq); - if (GNUNET_OK != - GNUNET_GNSRECORD_records_deserialize (data_size, - data, - record_count, - rd)) - { - GNUNET_break (0); - ret = GNUNET_SYSERR; - break; - } - else - { - if (NULL != zone_key) - zk = *zone_key; - if (NULL != iter) - iter (iter_cls, - seq, - &zk, - label, - record_count, - rd); - } - } - GNUNET_SQ_cleanup_result (rs); - } - } - GNUNET_SQ_reset (plugin->dbh, - stmt); - return ret; -} - - -/** - * Lookup records in the datastore for which we are the authority. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone - * @param label name of the record in the zone - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR - */ -static enum GNUNET_GenericReturnValue -namestore_sqlite_lookup_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_auto_from_type (zone), - GNUNET_SQ_query_param_string (label), - GNUNET_SQ_query_param_end - }; - - if (NULL == zone) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_SQ_bind (plugin->lookup_label, - params)) - { - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind_XXXX"); - GNUNET_SQ_reset (plugin->dbh, - plugin->lookup_label); - return GNUNET_SYSERR; - } - return get_records_and_call_iterator (plugin, - plugin->lookup_label, - zone, - 1, - iter, - iter_cls); -} - - -/** - * Iterate over the results for a particular key and zone in the - * datastore. Will return at most one result to the iterator. - * - * @param cls closure (internal context for the plugin) - * @param zone hash of public key of the zone, NULL to iterate over all zones - * @param serial serial number to exclude in the list of all matching records - * @param limit maximum number of results to return - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error - */ -static enum GNUNET_GenericReturnValue -namestore_sqlite_iterate_records (void *cls, - const struct - GNUNET_CRYPTO_PrivateKey *zone, - uint64_t serial, - uint64_t limit, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - sqlite3_stmt *stmt; - int err; - - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - if (NULL == zone) - { - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_uint64 (&serial), - GNUNET_SQ_query_param_uint64 (&limit), - GNUNET_SQ_query_param_end - }; - - stmt = plugin->iterate_all_zones; - err = GNUNET_SQ_bind (stmt, - params); - } - else - { - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_auto_from_type (zone), - GNUNET_SQ_query_param_uint64 (&serial), - GNUNET_SQ_query_param_uint64 (&limit), - GNUNET_SQ_query_param_end - }; - - stmt = plugin->iterate_zone; - err = GNUNET_SQ_bind (stmt, - params); - } - if (GNUNET_OK != err) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind_XXXX"); - GNUNET_SQ_reset (plugin->dbh, - stmt); - return GNUNET_SYSERR; - } - return get_records_and_call_iterator (plugin, - stmt, - zone, - limit, - iter, - iter_cls); -} - - -/** - * Look for an existing PKEY delegation record for a given public key. - * Returns at most one result to the iterator. - * - * @param cls closure (internal context for the plugin) - * @param zone private key of the zone to look up in, never NULL - * @param value_zone public key of the target zone (value), never NULL - * @param iter function to call with the result - * @param iter_cls closure for @a iter - * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error - */ -static enum GNUNET_GenericReturnValue -namestore_sqlite_zone_to_name (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const struct - GNUNET_CRYPTO_PublicKey *value_zone, - GNUNET_NAMESTORE_RecordIterator iter, - void *iter_cls) -{ - struct Plugin *plugin = cls; - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - struct GNUNET_SQ_QueryParam params[] = { - GNUNET_SQ_query_param_auto_from_type (zone), - GNUNET_SQ_query_param_auto_from_type (value_zone), - GNUNET_SQ_query_param_end - }; - - if (GNUNET_OK != - GNUNET_SQ_bind (plugin->zone_to_name, - params)) - { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_bind_XXXX"); - GNUNET_SQ_reset (plugin->dbh, - plugin->zone_to_name); - return GNUNET_SYSERR; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Performing reverse lookup for `%s'\n", - GNUNET_GNSRECORD_z2s (value_zone)); - return get_records_and_call_iterator (plugin, - plugin->zone_to_name, - zone, - 1, - iter, - iter_cls); -} - - -/** - * Begin a transaction for a client. - * This locks the database. SQLite is unable to discern between different - * rows with a specific zone key but the API looks like this anyway. - * https://www.sqlite.org/lang_transaction.html - * - * @param cls closure (internal context for the plugin) - * @param emsg error message set of return code is #GNUNET_SYSERR - * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. - */ -static enum GNUNET_GenericReturnValue -namestore_sqlite_transaction_begin (void *cls, - char **emsg) -{ - struct Plugin *plugin = cls; - int rc; - char *sqlEmsg; - - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - rc = sqlite3_exec (plugin->dbh, "BEGIN IMMEDIATE TRANSACTION;", - NULL, NULL, &sqlEmsg); - if (SQLITE_OK != rc) - { - *emsg = GNUNET_strdup (sqlEmsg); - sqlite3_free (sqlEmsg); - } - return (SQLITE_OK != rc) ? GNUNET_SYSERR : GNUNET_YES; -} - - -/** - * Commit a transaction for a client. - * This releases the lock on the database. - * - * @param cls closure (internal context for the plugin) - * @param emsg error message set of return code is #GNUNET_SYSERR - * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. - */ -static enum GNUNET_GenericReturnValue -namestore_sqlite_transaction_rollback (void *cls, - char **emsg) -{ - struct Plugin *plugin = cls; - int rc; - char *sqlEmsg; - - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - rc = sqlite3_exec (plugin->dbh, "ROLLBACK;", - NULL, NULL, &sqlEmsg); - if (SQLITE_OK != rc) - { - *emsg = GNUNET_strdup (sqlEmsg); - sqlite3_free (sqlEmsg); - } - return (SQLITE_OK != rc) ? GNUNET_SYSERR : GNUNET_YES; -} - - -/** - * Roll back a transaction for a client. - * This releases the lock on the database. - * - * @param cls closure (internal context for the plugin) - * @param emsg error message set of return code is #GNUNET_SYSERR - * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. - */ -static enum GNUNET_GenericReturnValue -namestore_sqlite_transaction_commit (void *cls, - char **emsg) -{ - struct Plugin *plugin = cls; - int rc; - char *sqlEmsg; - - GNUNET_assert (GNUNET_OK == database_prepare (plugin)); - rc = sqlite3_exec (plugin->dbh, "END TRANSACTION;", - NULL, NULL, &sqlEmsg); - if (SQLITE_OK != rc) - { - *emsg = GNUNET_strdup (sqlEmsg); - sqlite3_free (sqlEmsg); - } - return (SQLITE_OK != rc) ? GNUNET_SYSERR : GNUNET_YES; -} - - -static enum GNUNET_GenericReturnValue -namestore_sqlite_create_tables (void *cls) -{ - struct Plugin *plugin = cls; - struct GNUNET_SQ_ExecuteStatement es[] = { - GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"), - GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"), - GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"), - GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"), - GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""), - GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=NORMAL"), - GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"), - GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"), - GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns098records (" - " uid INTEGER PRIMARY KEY," - " zone_private_key BLOB NOT NULL," - " pkey BLOB," - " rvalue INT8 NOT NULL," - " record_count INT NOT NULL," - " record_data BLOB NOT NULL," - " label TEXT NOT NULL" - ")"), - GNUNET_SQ_make_try_execute ("CREATE INDEX ir_pkey_reverse " - "ON ns098records (zone_private_key,pkey)"), - GNUNET_SQ_make_try_execute ("CREATE INDEX ir_pkey_iter " - "ON ns098records (zone_private_key,uid)"), - GNUNET_SQ_EXECUTE_STATEMENT_END - }; - - if (GNUNET_OK != - GNUNET_SQ_exec_statements (plugin->dbh, - es)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to setup database with: `%s'\n", - sqlite3_errmsg (plugin->dbh)); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -static enum GNUNET_GenericReturnValue -namestore_sqlite_drop_tables (void *cls) -{ - struct Plugin *plugin = cls; - struct GNUNET_SQ_ExecuteStatement es_drop[] = { - GNUNET_SQ_make_execute ("DROP TABLE IF EXISTS ns098records"), - GNUNET_SQ_EXECUTE_STATEMENT_END - }; - - if (GNUNET_OK != - GNUNET_SQ_exec_statements (plugin->dbh, - es_drop)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to drop database with: `%s'\n", - sqlite3_errmsg (plugin->dbh)); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Initialize the database connections and associated - * data structures (create tables and indices - * as needed as well). - * - * @param plugin the plugin context (state for this module) - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -database_connect (struct Plugin *plugin) -{ - char *sqlite_filename; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, - "namestore-sqlite", - "FILENAME", - &sqlite_filename)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "namestore-sqlite", - "FILENAME"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_DISK_file_test (sqlite_filename)) - { - if (GNUNET_OK != - GNUNET_DISK_directory_create_for_file (sqlite_filename)) - { - GNUNET_break (0); - GNUNET_free (sqlite_filename); - return GNUNET_SYSERR; - } - } - - /* Open database and precompile statements */ - if ((NULL == plugin->dbh) && - (SQLITE_OK != sqlite3_open (sqlite_filename, - &plugin->dbh))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to initialize SQLite: %s.\n"), - sqlite3_errmsg (plugin->dbh)); - GNUNET_free (sqlite_filename); - return GNUNET_SYSERR; - } - GNUNET_free (sqlite_filename); - GNUNET_break (SQLITE_OK == - sqlite3_busy_timeout (plugin->dbh, - BUSY_TIMEOUT_MS)); - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, - "namestore-sqlite", - "INIT_ON_CONNECT")) - { - if (GNUNET_OK != - namestore_sqlite_create_tables (plugin)) - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Entry point for the plugin. - * - * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*" - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_namestore_sqlite_init (void *cls) -{ - struct Plugin *plugin; - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct GNUNET_NAMESTORE_PluginFunctions *api; - - plugin = GNUNET_new (struct Plugin); - plugin->cfg = cfg; - if (GNUNET_OK != database_connect (plugin)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Database could not be connected to.\n"); - GNUNET_free (plugin); - return NULL; - } - api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions); - api->cls = plugin; - api->store_records = &namestore_sqlite_store_records; - api->iterate_records = &namestore_sqlite_iterate_records; - api->zone_to_name = &namestore_sqlite_zone_to_name; - api->lookup_records = &namestore_sqlite_lookup_records; - api->transaction_begin = &namestore_sqlite_transaction_begin; - api->transaction_commit = &namestore_sqlite_transaction_commit; - api->transaction_rollback = &namestore_sqlite_transaction_rollback; - api->create_tables = &namestore_sqlite_create_tables; - api->drop_tables = &namestore_sqlite_drop_tables; - /** - * NOTE: Since SQlite does not support SELECT ... FOR UPDATE this is - * just an alias to lookup_records. The BEGIN IMMEDIATE mechanic currently - * implicitly ensures this API behaves as it should - */ - api->edit_records = &namestore_sqlite_lookup_records; - LOG (GNUNET_ERROR_TYPE_DEBUG, - _ ("SQlite database running\n")); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_namestore_sqlite_done (void *cls) -{ - struct GNUNET_NAMESTORE_PluginFunctions *api = cls; - struct Plugin *plugin = api->cls; - - database_shutdown (plugin); - plugin->cfg = NULL; - GNUNET_free (plugin); - GNUNET_free (api); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "SQlite plugin is finished\n"); - return NULL; -} - - -/* end of plugin_namestore_sqlite.c */ diff --git a/src/namestore/plugin_rest_namestore.c b/src/namestore/plugin_rest_namestore.c deleted file mode 100644 index 31e78e6dd..000000000 --- a/src/namestore/plugin_rest_namestore.c +++ /dev/null @@ -1,1364 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @author Martin Schanzenbach - * @author Philippe Buschmann - * @file namestore/plugin_rest_namestore.c - * @brief GNUnet Namestore REST plugin - */ - -#include "platform.h" -#include "gnunet_error_codes.h" -#include "gnunet_rest_plugin.h" -#include "gnunet_gns_service.h" -#include "gnunet_namestore_service.h" -#include "gnunet_identity_service.h" -#include "gnunet_rest_lib.h" -#include "gnunet_gnsrecord_json_lib.h" -#include "microhttpd.h" -#include - -/** - * Namestore namespace - */ -#define GNUNET_REST_API_NS_NAMESTORE "/namestore" - -/** - * Namestore import API namespace - */ -#define GNUNET_REST_API_NS_NAMESTORE_IMPORT "/namestore/import" - -/** - * State while collecting all egos - */ -#define ID_REST_STATE_INIT 0 - -/** - * Done collecting egos - */ -#define ID_REST_STATE_POST_INIT 1 -/** - * The configuration handle - */ -const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * HTTP methods allows for this plugin - */ -static char *allow_methods; - -/** - * Ego list - */ -static struct EgoEntry *ego_head; - -/** - * Ego list - */ -static struct EgoEntry *ego_tail; - -/** - * The processing state - */ -static int state; - -/** - * Handle to NAMESTORE - */ -static struct GNUNET_NAMESTORE_Handle *ns_handle; - -/** - * Handle to Identity service. - */ -static struct GNUNET_IDENTITY_Handle *identity_handle; - -/** - * @brief struct returned by the initialization function of the plugin - */ -struct Plugin -{ - const struct GNUNET_CONFIGURATION_Handle *cfg; -}; - -/** - * The default namestore ego - */ -struct EgoEntry -{ - /** - * DLL - */ - struct EgoEntry *next; - - /** - * DLL - */ - struct EgoEntry *prev; - - /** - * Ego Identifier - */ - char *identifier; - - /** - * Public key string - */ - char *keystring; - - /** - * The Ego - */ - struct GNUNET_IDENTITY_Ego *ego; -}; - - -enum UpdateStrategy -{ - UPDATE_STRATEGY_REPLACE, - UPDATE_STRATEGY_APPEND -}; - -/** - * The request handle - */ -struct RequestHandle -{ - /** - * DLL - */ - struct RequestHandle *next; - - /** - * DLL - */ - struct RequestHandle *prev; - - /** - * Records to store - */ - char *record_name; - - /** - * Record type filter - */ - uint32_t record_type; - - /** - * How to update the record set - */ - enum UpdateStrategy update_strategy; - - /** - * Records to store - */ - struct GNUNET_GNSRECORD_Data *rd; - - /** - * Number of records in rd - */ - unsigned int rd_count; - - /** - * RecordInfo array - */ - struct GNUNET_NAMESTORE_RecordInfo *ri; - - /** - * Size of record info - */ - unsigned int rd_set_count; - - /** - * Position of record info - */ - unsigned int rd_set_pos; - - /** - * NAMESTORE Operation - */ - struct GNUNET_NAMESTORE_QueueEntry *ns_qe; - - /** - * For bulk import, we need a dedicated Namestore handle - */ - struct GNUNET_NAMESTORE_Handle *nc; - - /** - * Response object - */ - json_t *resp_object; - - - /** - * Handle to NAMESTORE it - */ - struct GNUNET_NAMESTORE_ZoneIterator *list_it; - - /** - * Private key for the zone - */ - const struct GNUNET_CRYPTO_PrivateKey *zone_pkey; - - /** - * IDENTITY Operation - */ - struct EgoEntry *ego_entry; - - /** - * IDENTITY Operation - */ - struct GNUNET_IDENTITY_Operation *op; - - /** - * Rest connection - */ - struct GNUNET_REST_RequestHandle *rest_handle; - - /** - * Desired timeout for the lookup (default is no timeout). - */ - struct GNUNET_TIME_Relative timeout; - - /** - * ID of a task associated with the resolution process. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * The plugin result processor - */ - GNUNET_REST_ResultProcessor proc; - - /** - * The closure of the result processor - */ - void *proc_cls; - - /** - * The url - */ - char *url; - - /** - * Error code - */ - enum GNUNET_ErrorCode ec; - -}; - -/** - * DLL - */ -static struct RequestHandle *requests_head; - -/** - * DLL - */ -static struct RequestHandle *requests_tail; - - -/** - * Cleanup lookup handle - * @param cls Handle to clean up - */ -static void -cleanup_handle (void *cls) -{ - struct RequestHandle *handle = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n"); - if (NULL != handle->timeout_task) - { - GNUNET_SCHEDULER_cancel (handle->timeout_task); - handle->timeout_task = NULL; - } - if (NULL != handle->record_name) - GNUNET_free (handle->record_name); - if (NULL != handle->url) - GNUNET_free (handle->url); - if (NULL != handle->rd) - { - for (int i = 0; i < handle->rd_count; i++) - { - if (NULL != handle->rd[i].data) - GNUNET_free_nz ((void *) handle->rd[i].data); - } - GNUNET_free (handle->rd); - } - if (NULL != handle->timeout_task) - GNUNET_SCHEDULER_cancel (handle->timeout_task); - if (NULL != handle->list_it) - GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it); - if (NULL != handle->ns_qe) - GNUNET_NAMESTORE_cancel (handle->ns_qe); - if (NULL != handle->nc) - GNUNET_NAMESTORE_disconnect (handle->nc); - if (NULL != handle->resp_object) - { - json_decref (handle->resp_object); - } - GNUNET_CONTAINER_DLL_remove (requests_head, - requests_tail, - handle); - GNUNET_free (handle); -} - - -/** - * Task run on errors. Reports an error and cleans up everything. - * - * @param cls the `struct RequestHandle` - */ -static void -do_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - json_t *json_error = json_object (); - char *response; - const char* emsg; - int response_code; - - emsg = GNUNET_ErrorCode_get_hint (handle->ec); - json_object_set_new (json_error, "error", json_string (emsg)); - json_object_set_new (json_error, "error_code", json_integer (handle->ec)); - response_code = GNUNET_ErrorCode_get_http_status (handle->ec); - if (0 == response_code) - response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - response = json_dumps (json_error, 0); - resp = GNUNET_REST_create_response (response); - GNUNET_assert (MHD_YES == - MHD_add_response_header (resp, "Content-Type", - "application/json")); - handle->proc (handle->proc_cls, resp, response_code); - json_decref (json_error); - GNUNET_free (response); - cleanup_handle (handle); -} - - -/** - * Get EgoEntry from list with either a public key or a name - * If public key and name are not NULL, it returns the public key result first - * - * @param handle the RequestHandle - * @param pubkey the public key of an identity (only one can be NULL) - * @param name the name of an identity (only one can be NULL) - * @return EgoEntry or NULL if not found - */ -struct EgoEntry * -get_egoentry_namestore (struct RequestHandle *handle, char *name) -{ - struct EgoEntry *ego_entry; - char *copy = GNUNET_strdup (name); - char *tmp; - - if (NULL == name) - return NULL; - tmp = strtok (copy, "/"); - if (NULL == tmp) - return NULL; - for (ego_entry = ego_head; NULL != ego_entry; - ego_entry = ego_entry->next) - { - if (0 != strcasecmp (tmp, ego_entry->identifier)) - continue; - GNUNET_free (copy); - return ego_entry; - } - GNUNET_free (copy); - return NULL; -} - - -/** - * Does internal server error when iteration failed. - * - * @param cls the `struct RequestHandle` - */ -static void -namestore_iteration_error (void *cls) -{ - struct RequestHandle *handle = cls; - - handle->ec = GNUNET_EC_NAMESTORE_ITERATION_FAILED; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; -} - - -static void -create_finished (void *cls, enum GNUNET_ErrorCode ec) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - - handle->ns_qe = NULL; - handle->ec = ec; - if (GNUNET_EC_NONE != ec) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - resp = GNUNET_REST_create_response (NULL); - handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); -} - - -static void -del_finished (void *cls, enum GNUNET_ErrorCode ec) -{ - struct RequestHandle *handle = cls; - - handle->ns_qe = NULL; - handle->ec = ec; - if (GNUNET_EC_NONE != ec) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->proc (handle->proc_cls, - GNUNET_REST_create_response (NULL), - MHD_HTTP_NO_CONTENT); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); -} - - -/** - * Iteration over all results finished, build final - * response. - * - * @param cls the `struct RequestHandle` - */ -static void -namestore_list_finished (void *cls) -{ - struct RequestHandle *handle = cls; - char *result_str; - struct MHD_Response *resp; - - handle->list_it = NULL; - - if (NULL == handle->resp_object) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_EMPTY; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - result_str = json_dumps (handle->resp_object, 0); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); - resp = GNUNET_REST_create_response (result_str); - GNUNET_assert (MHD_YES == - MHD_add_response_header (resp, "Content-Type", - "application/json")); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_free (result_str); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); -} - - -/** - * Create a response with requested records - * - * @param handle the RequestHandle - */ -static void -namestore_list_iteration (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *rname, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd, - struct GNUNET_TIME_Absolute expiry) -{ - struct RequestHandle *handle = cls; - struct GNUNET_GNSRECORD_Data rd_filtered[rd_len]; - json_t *record_obj; - int i = 0; - int j = 0; - - if (rd_len == 0) - { - /** skip **/ - GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1); - return; - } - - for (i = 0; i < rd_len; i++) - { - if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) && - (rd[i].record_type != handle->record_type)) - continue; /* Apply filter */ - rd_filtered[j] = rd[i]; - rd_filtered[j].data = rd[i].data; - j++; - } - /** Only add if not empty **/ - if (j > 0) - { - if (NULL == handle->resp_object) - handle->resp_object = json_array (); - record_obj = GNUNET_GNSRECORD_JSON_from_gnsrecord (rname, - rd_filtered, - j); - json_array_append_new (handle->resp_object, record_obj); - } - GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1); -} - - -/** - * Handle lookup error - * - * @param cls the request handle - */ -static void -ns_lookup_error_cb (void *cls) -{ - struct RequestHandle *handle = cls; - - handle->ec = GNUNET_EC_NAMESTORE_LOOKUP_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, handle); -} - - -static void -ns_get_lookup_cb (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_len, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct RequestHandle *handle = cls; - struct GNUNET_GNSRECORD_Data rd_filtered[rd_len]; - int i = 0; - int j = 0; - - handle->ns_qe = NULL; - for (i = 0; i < rd_len; i++) - { - if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) && - (rd[i].record_type != handle->record_type)) - continue; /* Apply filter */ - rd_filtered[j] = rd[i]; - rd_filtered[j].data = rd[i].data; - j++; - } - /** Return 404 if no set was found **/ - if (j == 0) - { - handle->ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->resp_object = GNUNET_GNSRECORD_JSON_from_gnsrecord (label, - rd_filtered, - j); - GNUNET_SCHEDULER_add_now (&namestore_list_finished, handle); -} - - -/** - * Handle namestore GET request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -namestore_get (struct GNUNET_REST_RequestHandle *con_handle, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - struct GNUNET_HashCode key; - enum GNUNET_GNSRECORD_Filter filter_flags; - char *egoname; - char *labelname; - char *typename; - char *boolstring; - - egoname = NULL; - ego_entry = NULL; - - // set zone to name if given - if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url)) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1]; - ego_entry = get_egoentry_namestore (handle, egoname); - if (NULL == ego_entry) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - - GNUNET_CRYPTO_hash ("record_type", strlen ("record_type"), &key); - handle->record_type = GNUNET_GNSRECORD_TYPE_ANY; - if (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key)) - { - typename = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, - &key); - if (NULL != typename) - handle->record_type = GNUNET_GNSRECORD_typename_to_number (typename); - } - GNUNET_CRYPTO_hash ("omit_private", strlen ("omit_private"), &key); - filter_flags = GNUNET_GNSRECORD_FILTER_NONE; - if (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key)) - { - boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, - &key); - if ((0 == strcmp (boolstring, "1")) || - (0 == strcmp (boolstring, "yes")) || - (0 == strcmp (boolstring, "true"))) - filter_flags = GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE; - } - GNUNET_CRYPTO_hash ("include_maintenance", strlen ("include_maintenance"), - &key); - if (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key)) - { - boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, - &key); - if ((0 == strcmp (boolstring, "1")) || - (0 == strcmp (boolstring, "yes")) || - (0 == strcmp (boolstring, "true"))) - filter_flags |= GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE; - } - labelname = &egoname[strlen (ego_entry->identifier)]; - if (1 >= strlen (labelname)) - { - /* Iterate over all records */ - handle->list_it = - GNUNET_NAMESTORE_zone_iteration_start2 (ns_handle, - handle->zone_pkey, - &namestore_iteration_error, - handle, - &namestore_list_iteration, - handle, - &namestore_list_finished, - handle, - filter_flags); - if (NULL == handle->list_it) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - return; - } - handle->record_name = GNUNET_strdup (labelname + 1); - handle->ns_qe = GNUNET_NAMESTORE_records_lookup2 (ns_handle, - handle->zone_pkey, - handle->record_name, - &ns_lookup_error_cb, - handle, - &ns_get_lookup_cb, - handle, - filter_flags); - if (NULL == handle->ns_qe) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } -} - - -static void -ns_lookup_cb (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct RequestHandle *handle = cls; - struct GNUNET_GNSRECORD_Data rd_new[rd_count + handle->rd_count]; - int i = 0; - int j = 0; - - if (UPDATE_STRATEGY_APPEND == handle->update_strategy) - { - for (i = 0; i < rd_count; i++) - rd_new[i] = rd[i]; - } - for (j = 0; j < handle->rd_count; j++) - rd_new[i + j] = handle->rd[j]; - handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, - handle->zone_pkey, - handle->record_name, - i + j, - rd_new, - &create_finished, - handle); - if (NULL == handle->ns_qe) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } -} - - -static void -bulk_tx_commit_cb (void *cls, enum GNUNET_ErrorCode ec) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - - handle->ns_qe = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Commit finished (%d)\n", ec); - handle->ec = ec; - if (GNUNET_EC_NONE != ec) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - resp = GNUNET_REST_create_response (NULL); - handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); -} - - -static void -import_next_cb (void *cls, enum GNUNET_ErrorCode ec) -{ - struct RequestHandle *handle = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Import finished (%d)\n", ec); - handle->ns_qe = NULL; - handle->ec = ec; - if (GNUNET_EC_NONE != ec) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - unsigned int remaining = handle->rd_set_count - handle->rd_set_pos; - if (0 == remaining) - { - handle->ns_qe = GNUNET_NAMESTORE_transaction_commit (handle->nc, - &bulk_tx_commit_cb, - handle); - return; - } - unsigned int sent_rds = 0; - // Find the smallest set of records we can send with our message size - // restriction of 16 bit - handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc, - handle->zone_pkey, - remaining, - &handle->ri[handle-> - rd_set_pos], - &sent_rds, - &import_next_cb, - handle); - if ((NULL == handle->ns_qe) && (0 == sent_rds)) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->rd_set_pos += sent_rds; -} - -static void -bulk_tx_start (void *cls, enum GNUNET_ErrorCode ec) -{ - struct RequestHandle *handle = cls; - json_t *data_js; - json_error_t err; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Transaction started...\n"); - handle->ec = ec; - if (GNUNET_EC_NONE != ec) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - if (0 >= handle->rest_handle->data_size) - { - handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - char term_data[handle->rest_handle->data_size + 1]; - term_data[handle->rest_handle->data_size] = '\0'; - GNUNET_memcpy (term_data, - handle->rest_handle->data, - handle->rest_handle->data_size); - data_js = json_loads (term_data, JSON_DECODE_ANY, &err); - if (NULL == data_js) - { - handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error parsing data: %s", err.text); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - if (! json_is_array (data_js)) - { - handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; - GNUNET_SCHEDULER_add_now (&do_error, handle); - json_decref (data_js); - return; - } - handle->rd_set_count = json_array_size (data_js); - handle->ri = GNUNET_malloc (handle->rd_set_count - * sizeof (struct GNUNET_NAMESTORE_RecordInfo)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got record set of size %d\n", handle->rd_set_count); - char *albl; - size_t index; - json_t *value; - json_array_foreach (data_js, index, value) { - { - struct GNUNET_GNSRECORD_Data *rd; - struct GNUNET_JSON_Specification gnsspec[] = - { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&rd, - &handle->ri[index].a_rd_count, - &albl), - GNUNET_JSON_spec_end () }; - if (GNUNET_OK != GNUNET_JSON_parse (value, gnsspec, NULL, NULL)) - { - handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; - GNUNET_SCHEDULER_add_now (&do_error, handle); - json_decref (data_js); - return; - } - handle->ri[index].a_rd = rd; - handle->ri[index].a_label = albl; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Parsed record set for name %s\n", - handle->ri[index].a_label); - } - } - // json_decref (data_js); - - unsigned int sent_rds = 0; - // Find the smallest set of records we can send with our message size - // restriction of 16 bit - handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc, - handle->zone_pkey, - handle->rd_set_count, - handle->ri, - &sent_rds, - &import_next_cb, - handle); - if ((NULL == handle->ns_qe) && (0 == sent_rds)) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->rd_set_pos += sent_rds; -} - - -/** - * Handle namestore POST import - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -namestore_import (struct GNUNET_REST_RequestHandle *con_handle, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - char *egoname; - - // set zone to name if given - if (strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1 >= strlen ( - handle->url)) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - ego_entry = NULL; - - egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1]; - ego_entry = get_egoentry_namestore (handle, egoname); - - if (NULL == ego_entry) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - - // We need a per-client connection for a transactional bulk import - handle->nc = GNUNET_NAMESTORE_connect (cfg); - if (NULL == handle->nc) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->ns_qe = GNUNET_NAMESTORE_transaction_begin (handle->nc, - &bulk_tx_start, - handle); -} - -/** - * Handle namestore POST/PUT request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -namestore_add_or_update (struct GNUNET_REST_RequestHandle *con_handle, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - char *egoname; - json_t *data_js; - json_error_t err; - - char term_data[handle->rest_handle->data_size + 1]; - - if (0 >= handle->rest_handle->data_size) - { - handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - term_data[handle->rest_handle->data_size] = '\0'; - GNUNET_memcpy (term_data, - handle->rest_handle->data, - handle->rest_handle->data_size); - data_js = json_loads (term_data, JSON_DECODE_ANY, &err); - struct GNUNET_JSON_Specification gnsspec[] = - { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&handle->rd, &handle->rd_count, - &handle->record_name), - GNUNET_JSON_spec_end () }; - if (GNUNET_OK != GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL)) - { - handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; - GNUNET_SCHEDULER_add_now (&do_error, handle); - json_decref (data_js); - return; - } - GNUNET_JSON_parse_free (gnsspec); - if (0 >= strlen (handle->record_name)) - { - handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; - GNUNET_SCHEDULER_add_now (&do_error, handle); - json_decref (data_js); - return; - } - json_decref (data_js); - - egoname = NULL; - ego_entry = NULL; - - // set zone to name if given - if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url)) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1]; - ego_entry = get_egoentry_namestore (handle, egoname); - - if (NULL == ego_entry) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - handle->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle, - handle->zone_pkey, - handle->record_name, - &ns_lookup_error_cb, - handle, - &ns_lookup_cb, - handle); - if (NULL == handle->ns_qe) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } -} - - -/** - * Handle namestore PUT request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -namestore_update (struct GNUNET_REST_RequestHandle *con_handle, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - handle->update_strategy = UPDATE_STRATEGY_REPLACE; - namestore_add_or_update (con_handle, url, cls); -} - - -/** - * Handle namestore POST request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -namestore_add (struct GNUNET_REST_RequestHandle *con_handle, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - handle->update_strategy = UPDATE_STRATEGY_APPEND; - namestore_add_or_update (con_handle, url, cls); -} - - -/** - * Handle namestore DELETE request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -void -namestore_delete (struct GNUNET_REST_RequestHandle *con_handle, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct EgoEntry *ego_entry; - char *egoname; - char *labelname; - - egoname = NULL; - ego_entry = NULL; - - // set zone to name if given - if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url)) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1]; - ego_entry = get_egoentry_namestore (handle, egoname); - if (NULL == ego_entry) - { - handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - labelname = &egoname[strlen (ego_entry->identifier)]; - // set zone to name if given - if (1 >= strlen (labelname)) - { - /* label is only "/" */ - handle->ec = GNUNET_EC_NAMESTORE_NO_LABEL_GIVEN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - } - - handle->record_name = GNUNET_strdup (labelname + 1); - handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, - handle->zone_pkey, - handle->record_name, - 0, - NULL, - &del_finished, - handle); - if (NULL == handle->ns_qe) - { - handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } -} - - -/** - * Respond to OPTIONS request - * - * @param con_handle the connection handle - * @param url the url - * @param cls the RequestHandle - */ -static void -options_cont (struct GNUNET_REST_RequestHandle *con_handle, - const char *url, - void *cls) -{ - struct MHD_Response *resp; - struct RequestHandle *handle = cls; - - // independent of path return all options - resp = GNUNET_REST_create_response (NULL); - GNUNET_assert (MHD_YES == - MHD_add_response_header (resp, - "Access-Control-Allow-Methods", - allow_methods)); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); - return; -} - - -static void -list_ego (void *cls, - struct GNUNET_IDENTITY_Ego *ego, - void **ctx, - const char *identifier) -{ - struct EgoEntry *ego_entry; - struct GNUNET_CRYPTO_PublicKey pk; - - if ((NULL == ego) && (ID_REST_STATE_INIT == state)) - { - state = ID_REST_STATE_POST_INIT; - return; - } - if (NULL == ego) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Called with NULL ego\n"); - return; - } - if (ID_REST_STATE_INIT == state) - { - ego_entry = GNUNET_new (struct EgoEntry); - GNUNET_IDENTITY_ego_get_public_key (ego, &pk); - ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk); - ego_entry->ego = ego; - ego_entry->identifier = GNUNET_strdup (identifier); - GNUNET_CONTAINER_DLL_insert_tail (ego_head, - ego_tail, - ego_entry); - } - /* Ego renamed or added */ - if (identifier != NULL) - { - for (ego_entry = ego_head; NULL != ego_entry; - ego_entry = ego_entry->next) - { - if (ego_entry->ego == ego) - { - /* Rename */ - GNUNET_free (ego_entry->identifier); - ego_entry->identifier = GNUNET_strdup (identifier); - break; - } - } - if (NULL == ego_entry) - { - /* Add */ - ego_entry = GNUNET_new (struct EgoEntry); - GNUNET_IDENTITY_ego_get_public_key (ego, &pk); - ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk); - ego_entry->ego = ego; - ego_entry->identifier = GNUNET_strdup (identifier); - GNUNET_CONTAINER_DLL_insert_tail (ego_head, - ego_tail, - ego_entry); - } - } - else - { - /* Delete */ - for (ego_entry = ego_head; NULL != ego_entry; - ego_entry = ego_entry->next) - { - if (ego_entry->ego == ego) - break; - } - if (NULL == ego_entry) - return; /* Not found */ - - GNUNET_CONTAINER_DLL_remove (ego_head, - ego_tail, - ego_entry); - GNUNET_free (ego_entry->identifier); - GNUNET_free (ego_entry->keystring); - GNUNET_free (ego_entry); - return; - } - -} - - -/** - * Function processing the REST call - * - * @param method HTTP method - * @param url URL of the HTTP request - * @param data body of the HTTP request (optional) - * @param data_size length of the body - * @param proc callback function for the result - * @param proc_cls closure for callback function - * @return GNUNET_OK if request accepted - */ -static enum GNUNET_GenericReturnValue -rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle, - GNUNET_REST_ResultProcessor proc, - void *proc_cls) -{ - struct RequestHandle *handle = GNUNET_new (struct RequestHandle); - struct GNUNET_REST_RequestHandlerError err; - static const struct GNUNET_REST_RequestHandler handlers[] = - { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get }, - { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE_IMPORT, - &namestore_import }, - { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add }, - { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_update }, - { MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, - &namestore_delete }, - { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont }, - GNUNET_REST_HANDLER_END }; - - handle->ec = GNUNET_EC_NONE; - handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - handle->proc_cls = proc_cls; - handle->proc = proc; - handle->rest_handle = rest_handle; - handle->zone_pkey = NULL; - handle->timeout_task = - GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle); - handle->url = GNUNET_strdup (rest_handle->url); - if (handle->url[strlen (handle->url) - 1] == '/') - handle->url[strlen (handle->url) - 1] = '\0'; - GNUNET_CONTAINER_DLL_insert (requests_head, - requests_tail, - handle); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); - if (GNUNET_NO == - GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, - handle)) - { - cleanup_handle (handle); - return GNUNET_NO; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); - return GNUNET_YES; -} - - -/** - * Entry point for the plugin. - * - * @param cls Config info - * @return NULL on error, otherwise the plugin context - */ -void * -libgnunet_plugin_rest_namestore_init (void *cls) -{ - static struct Plugin plugin; - struct GNUNET_REST_Plugin *api; - - cfg = cls; - if (NULL != plugin.cfg) - return NULL; /* can only initialize once! */ - memset (&plugin, 0, sizeof(struct Plugin)); - plugin.cfg = cfg; - api = GNUNET_new (struct GNUNET_REST_Plugin); - api->cls = &plugin; - api->name = GNUNET_REST_API_NS_NAMESTORE; - api->process_request = &rest_process_request; - state = ID_REST_STATE_INIT; - GNUNET_asprintf (&allow_methods, - "%s, %s, %s, %s, %s", - MHD_HTTP_METHOD_GET, - MHD_HTTP_METHOD_POST, - MHD_HTTP_METHOD_PUT, - MHD_HTTP_METHOD_DELETE, - MHD_HTTP_METHOD_OPTIONS); - ns_handle = GNUNET_NAMESTORE_connect (cfg); - identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ( - "Namestore REST API initialized\n")); - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the plugin context (as returned by "init") - * @return always NULL - */ -void * -libgnunet_plugin_rest_namestore_done (void *cls) -{ - struct GNUNET_REST_Plugin *api = cls; - struct Plugin *plugin = api->cls; - struct RequestHandle *request; - struct EgoEntry *ego_entry; - struct EgoEntry *ego_tmp; - - plugin->cfg = NULL; - while (NULL != (request = requests_head)) - do_error (request); - if (NULL != identity_handle) - GNUNET_IDENTITY_disconnect (identity_handle); - if (NULL != ns_handle) - GNUNET_NAMESTORE_disconnect (ns_handle); - - for (ego_entry = ego_head; NULL != ego_entry;) - { - ego_tmp = ego_entry; - ego_entry = ego_entry->next; - GNUNET_free (ego_tmp->identifier); - GNUNET_free (ego_tmp->keystring); - GNUNET_free (ego_tmp); - } - - GNUNET_free (allow_methods); - GNUNET_free (api); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Namestore REST plugin is finished\n"); - return NULL; -} - - -/* end of plugin_rest_namestore.c */ diff --git a/src/namestore/test_common.c b/src/namestore/test_common.c deleted file mode 100644 index 4df24a7f7..000000000 --- a/src/namestore/test_common.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2019 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_common.c - * @brief common functions for testcase setup - */ -#include "platform.h" -#include - -/** - * test if we can load the plugin @a name. - */ -static int -TNC_test_plugin (const char *cfg_name) -{ - char *database; - char *db_lib_name; - struct GNUNET_NAMESTORE_PluginFunctions *db; - struct GNUNET_CONFIGURATION_Handle *cfg; - - cfg = GNUNET_CONFIGURATION_create (); - if (GNUNET_OK != - GNUNET_CONFIGURATION_load (cfg, - cfg_name)) - { - GNUNET_break (0); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "namestore", - "database", - &database)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No database backend configured\n"); - GNUNET_CONFIGURATION_destroy (cfg); - return GNUNET_SYSERR; - } - GNUNET_asprintf (&db_lib_name, - "libgnunet_plugin_namestore_%s", - database); - GNUNET_free (database); - db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); - if (NULL == db) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to load plugin `%s'\n", - db_lib_name); - } - else - { - if (GNUNET_OK != db->create_tables (db->cls)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error creating tables\n"); - return GNUNET_SYSERR; - } - if (GNUNET_OK != db->drop_tables (db->cls)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error dropping tables\n"); - return GNUNET_SYSERR; - } - GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db)); - } - GNUNET_free (db_lib_name); - GNUNET_CONFIGURATION_destroy (cfg); - if (NULL == db) - return GNUNET_NO; - return GNUNET_YES; -} - - -/** - * General setup logic for starting the tests. Obtains the @a - * plugin_name and initializes the @a cfg_name. - */ -#define SETUP_CFG2(file_template, plugin_name, cfg_name) \ - do \ - { \ - GNUNET_log_setup (__FILE__, "WARNING", NULL); \ - plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); \ - GNUNET_asprintf (&cfg_name, file_template, plugin_name); \ - if (! TNC_test_plugin (cfg_name)) \ - { \ - GNUNET_free (plugin_name); \ - GNUNET_free (cfg_name); \ - return 77; \ - } \ - GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TEST_HOME"); \ - } while (0) -/** - * General setup logic for starting the tests. Obtains the @a - * plugin_name and initializes the @a cfg_name. - */ -#define SETUP_CFG(plugin_name, cfg_name) \ - do \ - { \ - GNUNET_log_setup (__FILE__, "WARNING", NULL); \ - plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); \ - GNUNET_asprintf (&cfg_name, "test_namestore_api_%s.conf", plugin_name); \ - if (! TNC_test_plugin (cfg_name)) \ - { \ - GNUNET_free (plugin_name); \ - GNUNET_free (cfg_name); \ - return 77; \ - } \ - GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TEST_HOME"); \ - } while (0) diff --git a/src/namestore/test_hostkey b/src/namestore/test_hostkey deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/namestore/test_namestore_api.conf b/src/namestore/test_namestore_api.conf deleted file mode 100644 index 1648c7cae..000000000 --- a/src/namestore/test_namestore_api.conf +++ /dev/null @@ -1,43 +0,0 @@ -@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf -@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf - -[PATHS] -GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-namestore/ - -[namestore] -DATABASE = sqlite -START_ON_DEMAND = YES -#PREFIX = valgrind --track-origins=yes --log-file=/tmp/ns_tx.log -RETURN_ORPHANED = YES - -[namecache] -DATABASE = sqlite -START_ON_DEMAND = YES - -[zonemaster] -START_ON_DEMAND = YES -IMMEDIATE_START = NO - -[dht] -START_ON_DEMAND = YES -IMMEDIATE_START = NO - - -[zonemaster-monitor] -START_ON_DEMAND = YES -IMMEDIATE_START = YES - -[identity] -START_ON_DEMAND = YES - -[nse] -WORKBITS = 0 - -[rest] -BASIC_AUTH_ENABLED=NO -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/v_log - - - -[transport] -PLUGINS = diff --git a/src/namestore/test_namestore_api_edit_records.c b/src/namestore/test_namestore_api_edit_records.c deleted file mode 100644 index a6bce7c17..000000000 --- a/src/namestore/test_namestore_api_edit_records.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2022 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_edit_records.c - * @brief testcase for namestore_api.c: Multiple clients work with record set. - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TEST_RECORD_DATALEN 123 - -#define TEST_RECORD_DATA 'a' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_NAMESTORE_Handle *nsh2; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static int res; - -static int removed; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - -static int nonce = 0; - -static void -cleanup () -{ - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - if (NULL != nsqe) - { - GNUNET_NAMESTORE_cancel (nsqe); - nsqe = NULL; - } - cleanup (); - res = 1; -} - - -static void -end (void *cls) -{ - cleanup (); - res = 0; -} - -static void -lookup_it (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - GNUNET_assert (0 == rd_count); - GNUNET_SCHEDULER_add_now (&end, NULL); -} - -static void -fail_cb (void *cls) -{ - if (endbadly_task != NULL) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; -} - -static void -remove_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - nsqe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to roll back: `%s'\n"), - GNUNET_ErrorCode_get_hint (ec)); - if (NULL != endbadly_task) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, - NULL); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Rolled back, perform lookup\n"); - removed = GNUNET_YES; - if (NULL != endbadly_task) - GNUNET_SCHEDULER_cancel (endbadly_task); - GNUNET_SCHEDULER_add_now (&end, NULL); -} - -static void -fail_cb_lock (void *cls); - -static void -edit_cont_b (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - const char *name = cls; - /** - * We should probably never get here right at first. - * We may want to change the blocking of nsh2 so that we do get this - * eventually instead of the error callback above when locked. - */ - if (0 == nonce) - { - if (endbadly_task != NULL) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - - } - /* Abort transaction for B */ - nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh2, remove_cont, - (void *) name); -} - - -static void -commit_cont_a (void *cls, - enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - GNUNET_assert (NULL != cls); - nsqe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Namestore could not store record: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - if (endbadly_task != NULL) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - /** - * Try again for B - */ - nsqe = GNUNET_NAMESTORE_records_edit (nsh2, - &privkey, - name, - &fail_cb_lock, - (void *) name, - &edit_cont_b, - (void *) name); - - GNUNET_assert (NULL != nsqe); -} - -static void -fail_cb_lock (void *cls) -{ - const char *name = cls; - if (1 == nonce) - { - if (endbadly_task != NULL) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - nonce = 1; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Failed to aquire additional lock\n"); - /* Now, we stop the transaction for B */ - nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont_a, - (void *) name); -} - - -static void -begin_cont_b (void *cls, - enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - GNUNET_assert (GNUNET_EC_NONE == ec); - /** Now, we expect this to "hang" let's see how this behaves in practice. */ - nsqe = GNUNET_NAMESTORE_records_edit (nsh2, - &privkey, - name, - &fail_cb_lock, - (void *) name, - &edit_cont_b, - (void *) name); - - GNUNET_assert (NULL != nsqe); -} - - -static void -edit_cont (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - const char *name = cls; - - GNUNET_assert (1 == rd_count); - /* Now, we start a transaction for B */ - nsqe = GNUNET_NAMESTORE_transaction_begin (nsh2, begin_cont_b, (void *) name); -} - - -static void -begin_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - GNUNET_assert (GNUNET_EC_NONE == ec); - nsqe = GNUNET_NAMESTORE_records_edit (nsh, - &privkey, - name, - &fail_cb, - (void *) name, - &edit_cont, - (void *) name); - - GNUNET_assert (NULL != nsqe); -} - -static void -preload_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - GNUNET_assert (NULL != cls); - nsqe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Namestore could not store record: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - if (endbadly_task != NULL) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - /* We start transaction for A */ - nsqe = GNUNET_NAMESTORE_transaction_begin (nsh, begin_cont, (void *) name); - -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - struct GNUNET_GNSRECORD_Data rd; - const char *name = "dummy"; - - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - nsh = GNUNET_NAMESTORE_connect (cfg); - nsh2 = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - GNUNET_break (NULL != nsh2); - - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_key_get_public (&privkey, - &pubkey); - - removed = GNUNET_NO; - - rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us; - rd.record_type = TEST_RECORD_TYPE; - rd.data_size = TEST_RECORD_DATALEN; - rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); - rd.flags = 0; - memset ((char *) rd.data, - 'a', - TEST_RECORD_DATALEN); - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd, - &preload_cont, - (void *) name); - GNUNET_assert (NULL != nsqe); - GNUNET_free_nz ((void *) rd.data); - - /*nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont); - nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh, rollback_cont); Must also happen on disconnect - nsqe = GNUNET_NAMESTORE_records_edit (nsh, - &privkey, - name, - 1, - &rd, - &edit_cont, - (void *) name); - nsqe = GNUNET_NAMESTORE_records_insert_bulk (nsh, - count, - &rd, - & - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd, - &put_cont, - (void *) name);*/ - GNUNET_assert (NULL != nsqe); -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-remove", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_remove.c */ diff --git a/src/namestore/test_namestore_api_lookup_nick.c b/src/namestore/test_namestore_api_lookup_nick.c deleted file mode 100644 index 21fc1ef79..000000000 --- a/src/namestore/test_namestore_api_lookup_nick.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_lookup_nick.c - * @brief testcase for namestore_api.c: NICK records - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_gns_service.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TEST_RECORD_DATALEN 123 - -#define TEST_NICK "gnunettestnick" - -#define TEST_RECORD_DATA 'a' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static int res; - -static struct GNUNET_GNSRECORD_Data rd_orig; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - -// static const char * name = "dummy.dummy.gnunet"; -static const char *name = "d"; - -static char *record_data; - -static void -cleanup () -{ - GNUNET_free (record_data); - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - * @param tc scheduler context - */ -static void -endbadly (void *cls) -{ - if (NULL != nsqe) - { - GNUNET_NAMESTORE_cancel (nsqe); - nsqe = NULL; - } - cleanup (); - res = 1; -} - - -static void -end (void *cls) -{ - cleanup (); - res = 0; -} - - -static void -lookup_it (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - nsqe = NULL; - int c; - int found_record = GNUNET_NO; - int found_nick = GNUNET_NO; - - if (0 != GNUNET_memcmp (&privkey, zone)) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - if (NULL == label) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - if (0 != strcmp (label, name)) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - if (2 != rd_count) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - for (c = 0; c < rd_count; c++) - { - if (GNUNET_GNSRECORD_TYPE_NICK == rd[c].record_type) - { - if (rd[c].data_size != strlen (TEST_NICK) + 1) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - if (0 != (rd[c].flags & GNUNET_GNSRECORD_RF_PRIVATE)) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - if (0 != strcmp (rd[c].data, TEST_NICK)) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - found_nick = GNUNET_YES; - } - else - { - if (rd[c].record_type != TEST_RECORD_TYPE) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - if (rd[c].data_size != TEST_RECORD_DATALEN) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - if (0 != memcmp (rd[c].data, rd_orig.data, TEST_RECORD_DATALEN)) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - if (rd[c].flags != rd->flags) - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - found_record = GNUNET_YES; - } - } - - /* Done */ - if ((GNUNET_YES == found_nick) && (GNUNET_YES == found_record)) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - GNUNET_SCHEDULER_add_now (&end, NULL); - } - else - { - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - GNUNET_SCHEDULER_add_now (&endbadly, NULL); - } -} - - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); -} - - -static void -put_cont (void *cls, enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - nsqe = NULL; - GNUNET_assert (NULL != cls); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (ec == GNUNET_EC_NONE) ? "SUCCESS" : "FAIL"); - - if (GNUNET_EC_NONE != ec) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - /* Lookup */ - nsqe = GNUNET_NAMESTORE_records_lookup (nsh, - &privkey, - name, - &fail_cb, - NULL, - &lookup_it, - NULL); -} - - -static void -nick_cont (void *cls, enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Nick added : %s\n", - (ec == GNUNET_EC_NONE) ? "SUCCESS" : "FAIL"); - - rd_orig.expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; - rd_orig.record_type = TEST_RECORD_TYPE; - rd_orig.data_size = TEST_RECORD_DATALEN; - record_data = GNUNET_malloc (TEST_RECORD_DATALEN); - rd_orig.data = record_data; - rd_orig.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - memset ((char *) rd_orig.data, 'a', TEST_RECORD_DATALEN); - - nsqe = GNUNET_NAMESTORE_records_store (nsh, &privkey, - name, - 1, - &rd_orig, - &put_cont, (void *) name); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - struct GNUNET_GNSRECORD_Data rd; - - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_key_get_public (&privkey, - &pubkey); - - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - - memset (&rd, 0, sizeof(rd)); - rd.data = TEST_NICK; - rd.data_size = strlen (TEST_NICK) + 1; - rd.record_type = GNUNET_GNSRECORD_TYPE_NICK; - rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; - rd.flags |= GNUNET_GNSRECORD_RF_PRIVATE; - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - GNUNET_GNS_EMPTY_LABEL_AT, - 1, - &rd, - &nick_cont, - (void *) name); - - if (NULL == nsqe) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore cannot store no block\n")); - } -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-lookup-nick", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_store.c */ diff --git a/src/namestore/test_namestore_api_monitoring.c b/src/namestore/test_namestore_api_monitoring.c deleted file mode 100644 index 74dad3749..000000000 --- a/src/namestore/test_namestore_api_monitoring.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_monitoring.c - * @brief testcase for zone monitoring functionality: monitor first, then add records - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PrivateKey privkey2; - -static struct GNUNET_NAMESTORE_ZoneMonitor *zm; - -static int res; - -static char *s_name_1; - -static struct GNUNET_GNSRECORD_Data *s_rd_1; - -static char *s_name_2; - -static struct GNUNET_GNSRECORD_Data *s_rd_2; - -static char *s_name_3; - -static struct GNUNET_GNSRECORD_Data *s_rd_3; - -struct GNUNET_NAMESTORE_QueueEntry *ns_ops[3]; - - -static void -do_shutdown () -{ - if (NULL != zm) - { - GNUNET_NAMESTORE_zone_monitor_stop (zm); - zm = NULL; - } - if (NULL != ns_ops[0]) - { - GNUNET_NAMESTORE_cancel (ns_ops[0]); - ns_ops[0] = NULL; - } - if (NULL != ns_ops[1]) - { - GNUNET_NAMESTORE_cancel (ns_ops[1]); - ns_ops[1] = NULL; - } - if (NULL != ns_ops[2]) - { - GNUNET_NAMESTORE_cancel (ns_ops[2]); - ns_ops[2] = NULL; - } - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_free (s_name_1); - GNUNET_free (s_name_2); - GNUNET_free (s_name_3); - - if (s_rd_1 != NULL) - { - GNUNET_free_nz ((void *) s_rd_1->data); - GNUNET_free (s_rd_1); - } - if (s_rd_2 != NULL) - { - GNUNET_free_nz ((void *) s_rd_2->data); - GNUNET_free (s_rd_2); - } - if (s_rd_3 != NULL) - { - GNUNET_free_nz ((void *) s_rd_3->data); - GNUNET_free (s_rd_3); - } -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - do_shutdown (); - res = 1; -} - - -static void -end (void *cls) -{ - do_shutdown (); - res = 0; -} - - -static void -zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *name, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - static int returned_records; - static int fail = GNUNET_NO; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Comparing results name %s\n", - name); - if (0 != GNUNET_memcmp (zone_key, - &privkey)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Monitoring returned wrong zone key\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - if (0 == strcmp (name, s_name_1)) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) - { - GNUNET_break (0); - fail = GNUNET_YES; - } - } - else if (0 == strcmp (name, s_name_2)) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) - { - GNUNET_break (0); - fail = GNUNET_YES; - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Invalid name %s\n", - name); - GNUNET_break (0); - fail = GNUNET_YES; - } - GNUNET_NAMESTORE_zone_monitor_next (zm, - 1); - if (2 == ++returned_records) - { - if (endbadly_task != NULL) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - } - if (GNUNET_YES == fail) - GNUNET_SCHEDULER_add_now (&endbadly, NULL); - else - GNUNET_SCHEDULER_add_now (&end, NULL); - } -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - static int c = 0; - char *label = cls; - - if (0 == strcmp (label, s_name_1)) - ns_ops[0] = NULL; - else if (0 == strcmp (label, s_name_2)) - ns_ops[1] = NULL; - else if (0 == strcmp (label, s_name_3)) - ns_ops[2] = NULL; - - if (GNUNET_EC_NONE == ec) - { - c++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record %u: `%s'\n", - c, - label); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create record `%s'\n", - label); - GNUNET_break (0); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, - NULL); - } -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_new_array (count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int c = 0; c < count; c++) - { - rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd[c].record_type = TEST_RECORD_TYPE; - rd[c].data_size = 50; - rd[c].data = GNUNET_malloc (50); - rd[c].flags = 0; - memset ((char *) rd[c].data, 'a', 50); - } - return rd; -} - - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); -} - - -static void -sync_cb (void *cls) -{ - /* do nothing */ -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - res = 1; - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - /* Start monitoring */ - zm = GNUNET_NAMESTORE_zone_monitor_start (cfg, - &privkey, - GNUNET_YES, - &fail_cb, - NULL, - &zone_proc, - NULL, - &sync_cb, - NULL); - if (NULL == zm) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone monitor\n"); - GNUNET_break (0); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &endbadly, NULL); - /* Connect to namestore */ - nsh = GNUNET_NAMESTORE_connect (cfg); - if (NULL == nsh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connect to namestore\n"); - GNUNET_break (0); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 3\n"); - /* name in different zone */ - GNUNET_asprintf (&s_name_3, "dummy3"); - s_rd_3 = create_record (1); - GNUNET_assert (NULL != (ns_ops[2] = - GNUNET_NAMESTORE_records_store (nsh, - &privkey2, - s_name_3, - 1, - s_rd_3, - &put_cont, - s_name_3))); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 1\n"); - GNUNET_asprintf (&s_name_1, "dummy1"); - s_rd_1 = create_record (1); - GNUNET_assert (NULL != (ns_ops[0] = - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_1, - 1, - s_rd_1, - &put_cont, - s_name_1))); - - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record 2 \n"); - GNUNET_asprintf (&s_name_2, "dummy2"); - s_rd_2 = create_record (1); - GNUNET_assert (NULL != (ns_ops[1] = - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_2, - 1, - s_rd_2, - &put_cont, - s_name_2))); -} - - -#include "test_common.c" - - -int -main (int argc, - char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-monitoring", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_monitoring.c */ diff --git a/src/namestore/test_namestore_api_monitoring_existing.c b/src/namestore/test_namestore_api_monitoring_existing.c deleted file mode 100644 index fe17833c8..000000000 --- a/src/namestore/test_namestore_api_monitoring_existing.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_monitoring_existing.c - * @brief testcase for zone monitoring functionality: add records first, then monitor - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) - -static const struct GNUNET_CONFIGURATION_Handle *cfg; - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PrivateKey privkey2; - -static struct GNUNET_NAMESTORE_ZoneMonitor *zm; - -static int res; - -static const char *s_name_1; - -static struct GNUNET_GNSRECORD_Data *s_rd_1; - -static const char *s_name_2; - -static struct GNUNET_GNSRECORD_Data *s_rd_2; - -static const char *s_name_3; - -static struct GNUNET_GNSRECORD_Data *s_rd_3; - -struct GNUNET_NAMESTORE_QueueEntry *ns_ops[3]; - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - endbadly_task = NULL; - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - res = 1; -} - - -static void -end (void *cls) -{ - if (NULL != zm) - { - GNUNET_NAMESTORE_zone_monitor_stop (zm); - zm = NULL; - } - if (NULL != ns_ops[0]) - { - GNUNET_NAMESTORE_cancel (ns_ops[0]); - ns_ops[0] = NULL; - } - if (NULL != ns_ops[1]) - { - GNUNET_NAMESTORE_cancel (ns_ops[1]); - ns_ops[1] = NULL; - } - if (NULL != ns_ops[2]) - { - GNUNET_NAMESTORE_cancel (ns_ops[2]); - ns_ops[2] = NULL; - } - if (NULL != endbadly_task) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - } - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - if (NULL != s_rd_1) - { - GNUNET_free_nz ((void *) s_rd_1->data); - GNUNET_free (s_rd_1); - } - if (NULL != s_rd_2) - { - GNUNET_free_nz ((void *) s_rd_2->data); - GNUNET_free (s_rd_2); - } - if (NULL != s_rd_3) - { - GNUNET_free_nz ((void *) s_rd_3->data); - GNUNET_free (s_rd_3); - } -} - - -static void -zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *name, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - static int returned_records; - static int fail = GNUNET_NO; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Comparing results name %s\n", - name); - if (0 != GNUNET_memcmp (zone_key, - &privkey)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Monitoring returned wrong zone key\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (0 == strcmp (name, - s_name_1)) - { - if (GNUNET_YES != - GNUNET_GNSRECORD_records_cmp (rd, - s_rd_1)) - { - GNUNET_break (0); - fail = GNUNET_YES; - } - } - else if (0 == strcmp (name, - s_name_2)) - { - if (GNUNET_YES != - GNUNET_GNSRECORD_records_cmp (rd, - s_rd_2)) - { - GNUNET_break (0); - fail = GNUNET_YES; - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Invalid name %s\n", - name); - GNUNET_break (0); - fail = GNUNET_YES; - } - GNUNET_NAMESTORE_zone_monitor_next (zm, - 1); - if (2 == ++returned_records) - { - GNUNET_SCHEDULER_shutdown (); - if (GNUNET_YES == fail) - { - GNUNET_break (0); - res = 1; - } - else - { - res = 0; - } - } -} - - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); -} - - -static void -sync_cb (void *cls) -{ - /* do nothing */ -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - static int c = 0; - const char *label = cls; - - if (0 == strcmp (label, - s_name_1)) - ns_ops[0] = NULL; - else if (0 == strcmp (label, - s_name_2)) - ns_ops[1] = NULL; - else if (0 == strcmp (label, - s_name_3)) - ns_ops[2] = NULL; - - if (GNUNET_EC_NONE == ec) - { - c++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record %u: `%s'\n", - c, - label); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to created records\n"); - GNUNET_break (0); - res = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_new_array (count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int c = 0; c < count; c++) - { - rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd[c].record_type = TEST_RECORD_TYPE; - rd[c].data_size = 50; - rd[c].data = GNUNET_malloc (50); - rd[c].flags = 0; - memset ((char *) rd[c].data, - 'a', - 50); - } - return rd; -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *mycfg, - struct GNUNET_TESTING_Peer *peer) -{ - res = 1; - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); - - cfg = mycfg; - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - /* Connect to namestore */ - nsh = GNUNET_NAMESTORE_connect (cfg); - if (NULL == nsh) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Connect to namestore failed\n"); - GNUNET_break (0); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, - NULL); - return; - } - /* Start monitoring */ - zm = GNUNET_NAMESTORE_zone_monitor_start (cfg, - &privkey, - GNUNET_YES, - &fail_cb, - NULL, - &zone_proc, - NULL, - &sync_cb, - NULL); - if (NULL == zm) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone monitor\n"); - GNUNET_break (0); - res = 1; - GNUNET_SCHEDULER_shutdown (); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 3\n"); - /* name in different zone */ - s_name_3 = "dummy3"; - s_rd_3 = create_record (1); - GNUNET_assert (NULL != (ns_ops[2] = - GNUNET_NAMESTORE_records_store (nsh, - &privkey2, - s_name_3, - 1, - s_rd_3, - &put_cont, - (void *) s_name_3))); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 1\n"); - s_name_1 = "dummy1"; - s_rd_1 = create_record (1); - GNUNET_assert (NULL != (ns_ops[0] = - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_1, - 1, - s_rd_1, - &put_cont, - (void *) s_name_1))); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 2 \n"); - s_name_2 = "dummy2"; - s_rd_2 = create_record (1); - GNUNET_assert (NULL != (ns_ops[1] = - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_2, - 1, - s_rd_2, - &put_cont, - (void *) s_name_2))); -} - - -#include "test_common.c" - - -int -main (int argc, - char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-monitoring-existing", - cfg_name, - &run, - NULL)) - { - GNUNET_break (0); - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_monitoring_existing.c */ diff --git a/src/namestore/test_namestore_api_postgres.conf b/src/namestore/test_namestore_api_postgres.conf deleted file mode 100644 index 007168280..000000000 --- a/src/namestore/test_namestore_api_postgres.conf +++ /dev/null @@ -1,10 +0,0 @@ -@INLINE@ test_namestore_api.conf - -[namestore] -DATABASE = postgres - - -[namestore-postgres] -CONFIG = connect_timeout=10 dbname=gnunetcheck -TEMPORARY_TABLE = NO -INIT_ON_CONNECT = YES diff --git a/src/namestore/test_namestore_api_remove.c b/src/namestore/test_namestore_api_remove.c deleted file mode 100644 index 1a4a7c867..000000000 --- a/src/namestore/test_namestore_api_remove.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api.c - * @brief testcase for namestore_api.c to: remove record - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TEST_RECORD_DATALEN 123 - -#define TEST_RECORD_DATA 'a' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static int res; - -static int removed; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - - -static void -cleanup () -{ - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - if (NULL != nsqe) - { - GNUNET_NAMESTORE_cancel (nsqe); - nsqe = NULL; - } - cleanup (); - res = 1; -} - - -static void -end (void *cls) -{ - cleanup (); - res = 0; -} - - -static void -remove_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - nsqe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Records could not be removed: `%s'\n"), - GNUNET_ErrorCode_get_hint (ec)); - if (NULL != endbadly_task) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, - NULL); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Records were removed, perform lookup\n"); - removed = GNUNET_YES; - if (NULL != endbadly_task) - GNUNET_SCHEDULER_cancel (endbadly_task); - GNUNET_SCHEDULER_add_now (&end, NULL); -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - GNUNET_assert (NULL != cls); - nsqe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Namestore could not store record: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - if (endbadly_task != NULL) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 0, NULL, - &remove_cont, (void *) name); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - struct GNUNET_GNSRECORD_Data rd; - const char *name = "dummy"; - - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_key_get_public (&privkey, - &pubkey); - - removed = GNUNET_NO; - - rd.expiration_time = GNUNET_TIME_UNIT_MINUTES.rel_value_us; - rd.record_type = TEST_RECORD_TYPE; - rd.data_size = TEST_RECORD_DATALEN; - rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); - rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - memset ((char *) rd.data, - 'a', - TEST_RECORD_DATALEN); - - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd, - &put_cont, - (void *) name); - if (NULL == nsqe) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore cannot store no block\n")); - } - GNUNET_free_nz ((void *) rd.data); -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-remove", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_remove.c */ diff --git a/src/namestore/test_namestore_api_remove_not_existing_record.c b/src/namestore/test_namestore_api_remove_not_existing_record.c deleted file mode 100644 index 11a69bea1..000000000 --- a/src/namestore/test_namestore_api_remove_not_existing_record.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_remove_not_existing_record.c - * @brief testcase for namestore_api.c - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE 1234 - -#define TEST_RECORD_DATALEN 123 - -#define TEST_RECORD_DATA 'a' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static int res; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - - -static void -cleanup (void) -{ - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - if (NULL != nsqe) - { - GNUNET_NAMESTORE_cancel (nsqe); - nsqe = NULL; - } - cleanup (); - res = 1; -} - - -static void -end (void *cls) -{ - cleanup (); - res = 0; -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - GNUNET_assert (NULL != cls); - nsqe = NULL; - if (endbadly_task != NULL) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - } - switch (ec) - { - case GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND: - /* We expect that the record is not found */ - GNUNET_SCHEDULER_add_now (&end, NULL); - break; - - case GNUNET_EC_NONE: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Namestore could remove non-existing record: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - GNUNET_SCHEDULER_add_now (&endbadly, NULL); - break; - - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Namestore failed: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - GNUNET_SCHEDULER_add_now (&endbadly, NULL); - break; - } -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - const char *name = "dummy"; - - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_key_get_public (&privkey, &pubkey); - - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 0, NULL, - &put_cont, (void *) name); - if (NULL == nsqe) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore cannot store no block\n")); - } -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-remove-non-existing-record", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_remove_not_existing_record.c */ diff --git a/src/namestore/test_namestore_api_sqlite.conf b/src/namestore/test_namestore_api_sqlite.conf deleted file mode 100644 index 342356247..000000000 --- a/src/namestore/test_namestore_api_sqlite.conf +++ /dev/null @@ -1,9 +0,0 @@ -@INLINE@ test_namestore_api.conf - -[namestore] -DATABASE = sqlite -# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/v_log - -[namestore-sqlite] -FILENAME = $GNUNET_TEST_HOME/namestore/sqlite_test.db -INIT_ON_CONNECT = YES diff --git a/src/namestore/test_namestore_api_store.c b/src/namestore/test_namestore_api_store.c deleted file mode 100644 index 22b92fbe5..000000000 --- a/src/namestore/test_namestore_api_store.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_store.c - * @brief testcase for namestore_api.c: store a record - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TEST_RECORD_DATALEN 123 - -#define TEST_RECORD_DATA 'a' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static int res; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - - -static void -cleanup () -{ - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - if (NULL != nsqe) - { - GNUNET_NAMESTORE_cancel (nsqe); - nsqe = NULL; - } - cleanup (); - res = 1; -} - - -static void -end (void *cls) -{ - cleanup (); - res = 0; -} - - -static void -put_cont (void *cls, enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - nsqe = NULL; - GNUNET_assert (NULL != cls); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - GNUNET_SCHEDULER_add_now (&end, NULL); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - struct GNUNET_GNSRECORD_Data rd; - const char *name = "dummy.dummy.gnunet"; - - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, NULL); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_key_get_public (&privkey, &pubkey); - - - rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us; - rd.record_type = TEST_RECORD_TYPE; - rd.data_size = TEST_RECORD_DATALEN; - rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); - rd.flags = 0; - memset ((char *) rd.data, 'a', TEST_RECORD_DATALEN); - - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd, - &put_cont, - (void *) name); - if (NULL == nsqe) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore cannot store no block\n")); - } - GNUNET_free_nz ((void *) rd.data); -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_store.c */ diff --git a/src/namestore/test_namestore_api_store_update.c b/src/namestore/test_namestore_api_store_update.c deleted file mode 100644 index 86495e261..000000000 --- a/src/namestore/test_namestore_api_store_update.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012, 2013, 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_store_update.c - * @brief testcase for namestore_api.c: store a record, update it and perform a lookup - * @author Matthias Wachs - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TEST_RECORD_DATALEN 123 - -#define TEST_RECORD_DATA 'a' - -#define TEST_RECORD_DATALEN2 234 - -#define TEST_RECORD_DATA2 'b' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static int res; - -static int update_performed; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - -static const char *name = "dummy"; - - -/** - * Terminate test with error. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - GNUNET_break (0); - endbadly_task = NULL; - GNUNET_SCHEDULER_shutdown (); - res = 1; -} - - -static void -end (void *cls) -{ - if (NULL != endbadly_task) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - } - if (NULL != nsqe) - { - GNUNET_NAMESTORE_cancel (nsqe); - nsqe = NULL; - } - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec); - - -static void -lookup_success (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char* label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct GNUNET_GNSRECORD_Data rd_new; - - GNUNET_assert (1 == rd_count); - GNUNET_assert (NULL != rd); - nsqe = NULL; - if (GNUNET_NO == update_performed) - { - char rd_cmp_data[TEST_RECORD_DATALEN]; - - memset (rd_cmp_data, - TEST_RECORD_DATA, - TEST_RECORD_DATALEN); - GNUNET_assert (TEST_RECORD_TYPE == rd[0].record_type); - GNUNET_assert (TEST_RECORD_DATALEN == rd[0].data_size); - GNUNET_assert (0 == memcmp (&rd_cmp_data, - rd[0].data, - TEST_RECORD_DATALEN)); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Block was decrypted successfully, updating record \n"); - - rd_new.flags = GNUNET_GNSRECORD_RF_NONE; - rd_new.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us - + 1000000000; - rd_new.record_type = TEST_RECORD_TYPE; - rd_new.data_size = TEST_RECORD_DATALEN2; - rd_new.data = GNUNET_malloc (TEST_RECORD_DATALEN2); - memset ((char *) rd_new.data, - TEST_RECORD_DATA2, - TEST_RECORD_DATALEN2); - - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd_new, - &put_cont, - (void *) name); - GNUNET_free (rd_new.data); - update_performed = GNUNET_YES; - } - else - { - char rd_cmp_data[TEST_RECORD_DATALEN2]; - - memset (rd_cmp_data, - TEST_RECORD_DATA2, - TEST_RECORD_DATALEN2); - GNUNET_assert (TEST_RECORD_TYPE == rd[0].record_type); - GNUNET_assert (TEST_RECORD_DATALEN2 == rd[0].data_size); - GNUNET_assert (0 == memcmp (&rd_cmp_data, - rd[0].data, - TEST_RECORD_DATALEN2)); - GNUNET_SCHEDULER_shutdown (); - res = 0; - } -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - struct GNUNET_HashCode derived_hash; - - nsqe = NULL; - GNUNET_assert (NULL != cls); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - /* Create derived hash */ - GNUNET_GNSRECORD_query_from_private_key (&privkey, - name, - &derived_hash); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Looking in namestore for `%s'\n", - GNUNET_h2s (&derived_hash)); - nsqe = GNUNET_NAMESTORE_records_lookup (nsh, - &privkey, - name, - &endbadly, - (void *) name, - & lookup_success, - (void *) name); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - struct GNUNET_GNSRECORD_Data rd; - - update_performed = GNUNET_NO; - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - memset (&privkey, 0, sizeof (privkey)); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_key_get_public (&privkey, &pubkey); - rd.flags = GNUNET_GNSRECORD_RF_NONE; - rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us + 1000000000; - rd.record_type = TEST_RECORD_TYPE; - rd.data_size = TEST_RECORD_DATALEN; - rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); - memset ((char *) rd.data, - TEST_RECORD_DATA, - TEST_RECORD_DATALEN); - - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd, - &put_cont, - (void *) name); - if (NULL == nsqe) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore cannot store no block\n")); - } - GNUNET_free_nz ((void *) rd.data); -} - - -#include "test_common.c" - - -int -main (int argc, - char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test--store-update", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_store_update.c */ diff --git a/src/namestore/test_namestore_api_tx_rollback.c b/src/namestore/test_namestore_api_tx_rollback.c deleted file mode 100644 index 4a701f60e..000000000 --- a/src/namestore/test_namestore_api_tx_rollback.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2022 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_tx_rollback.c - * @brief testcase for namestore_api_tx_rollback.c to: rollback changes in TX - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TEST_RECORD_DATALEN 123 - -#define TEST_RECORD_DATA 'a' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static int res; - -static int removed; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - - -static void -cleanup () -{ - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - if (NULL != nsqe) - { - GNUNET_NAMESTORE_cancel (nsqe); - nsqe = NULL; - } - cleanup (); - res = 1; -} - - -static void -end (void *cls) -{ - cleanup (); - res = 0; -} - -static void -lookup_it (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - GNUNET_assert (0 == rd_count); - GNUNET_SCHEDULER_add_now (&end, NULL); -} - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); -} - -static void -remove_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - nsqe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Unable to roll back: `%s'\n"), - GNUNET_ErrorCode_get_hint (ec)); - if (NULL != endbadly_task) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, - NULL); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Rolled back, perform lookup\n"); - removed = GNUNET_YES; - if (NULL != endbadly_task) - GNUNET_SCHEDULER_cancel (endbadly_task); - /* FIXME not actually doing lookup here */ - nsqe = GNUNET_NAMESTORE_records_lookup (nsh, - &privkey, - (char*) cls, - &fail_cb, - NULL, - &lookup_it, - NULL); -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - const char *name = cls; - - GNUNET_assert (NULL != cls); - nsqe = NULL; - if (GNUNET_EC_NONE != ec) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Namestore could not store record: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - if (endbadly_task != NULL) - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); - return; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh, remove_cont, - (void *) name); -} - -static void -begin_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - struct GNUNET_GNSRECORD_Data rd; - const char *name = cls; - - GNUNET_assert (GNUNET_EC_NONE == ec); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_key_get_public (&privkey, - &pubkey); - - removed = GNUNET_NO; - - rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us; - rd.record_type = TEST_RECORD_TYPE; - rd.data_size = TEST_RECORD_DATALEN; - rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); - rd.flags = 0; - memset ((char *) rd.data, - 'a', - TEST_RECORD_DATALEN); - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd, - &put_cont, - (void *) name); - GNUNET_assert (NULL != nsqe); - GNUNET_free_nz ((void *) rd.data); -} - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - const char *name = "dummy"; - - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - nsqe = GNUNET_NAMESTORE_transaction_begin (nsh, begin_cont, (void *) name); - /*nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont); - nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh, rollback_cont); Must also happen on disconnect - nsqe = GNUNET_NAMESTORE_records_edit (nsh, - &privkey, - name, - 1, - &rd, - &edit_cont, - (void *) name); - nsqe = GNUNET_NAMESTORE_records_insert_bulk (nsh, - count, - &rd, - & - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - name, - 1, - &rd, - &put_cont, - (void *) name);*/ - GNUNET_assert (NULL != nsqe); -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-remove", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_remove.c */ diff --git a/src/namestore/test_namestore_api_zone_iteration.c b/src/namestore/test_namestore_api_zone_iteration.c deleted file mode 100644 index fb69fffcc..000000000 --- a/src/namestore/test_namestore_api_zone_iteration.c +++ /dev/null @@ -1,464 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2018 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_zone_iteration.c - * @brief testcase for zone iteration functionality: iterate all zones - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PrivateKey privkey2; - -static struct GNUNET_NAMESTORE_ZoneIterator *zi; - -static int res; - -static int returned_records; - -static char *s_name_1; - -static struct GNUNET_GNSRECORD_Data *s_rd_1; - -static char *s_name_2; - -static struct GNUNET_GNSRECORD_Data *s_rd_2; - -static char *s_name_3; - -static struct GNUNET_GNSRECORD_Data *s_rd_3; - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - * @param tc scheduler context - */ -static void -endbadly (void *cls) -{ - endbadly_task = NULL; - GNUNET_SCHEDULER_shutdown (); - res = 1; -} - - -static void -end (void *cls) -{ - if (NULL != zi) - { - GNUNET_NAMESTORE_zone_iteration_stop (zi); - zi = NULL; - } - if (NULL != endbadly_task) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - } - GNUNET_free (s_name_1); - GNUNET_free (s_name_2); - GNUNET_free (s_name_3); - if (NULL != s_rd_1) - { - GNUNET_free_nz ((void *) s_rd_1->data); - GNUNET_free (s_rd_1); - } - if (NULL != s_rd_2) - { - GNUNET_free_nz ((void *) s_rd_2->data); - GNUNET_free (s_rd_2); - } - if (NULL != s_rd_3) - { - GNUNET_free_nz ((void *) s_rd_3->data); - GNUNET_free (s_rd_3); - } - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } -} - - -static void -zone_end (void *cls) -{ - GNUNET_break (3 == returned_records); - if (3 == returned_records) - { - res = 0; /* Last iteraterator callback, we are done */ - zi = NULL; - } - else - res = 1; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received last result, iteration done after receing %u results\n", - returned_records); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); -} - - -static void -zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - int failed = GNUNET_NO; - - GNUNET_assert (NULL != zone); - if (0 == GNUNET_memcmp (zone, - &privkey)) - { - if (0 == strcmp (label, s_name_1)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else if (0 == strcmp (label, s_name_2)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid record count\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Comparing result failed: got name `%s' for first zone\n", - label); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else if (0 == GNUNET_memcmp (zone, - &privkey2)) - { - if (0 == strcmp (label, s_name_3)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_3)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid record count\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Comparing result failed: got name `%s' for first zone\n", - label); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid zone\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - - if (failed == GNUNET_NO) - { - returned_records++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Telling namestore to send the next result\n"); - GNUNET_NAMESTORE_zone_iterator_next (zi, - 1); - } - else - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - res = 1; - } -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - static int c = 0; - - if (GNUNET_EC_NONE == ec) - { - c++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record %u \n", - c); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to created records: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - res = 1; - return; - } - - if (c == 3) - { - res = 1; - returned_records = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All records created, starting iteration over all zones \n"); - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &zone_proc, - NULL, - &zone_end, - NULL); - if (zi == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone iterator\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - res = 1; - return; - } - } -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_new_array (count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int c = 0; c < count; c++) - { - rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd[c].record_type = TEST_RECORD_TYPE; - rd[c].data_size = 50; - rd[c].data = GNUNET_malloc (50); - rd[c].flags = 0; - memset ((char *) rd[c].data, 'a', 50); - } - return rd; -} - - -/** - * Callback called from the zone iterator when we iterate over - * the empty zone. Check that we got no records and then - * start the actual tests by filling the zone. - */ -static void -empty_zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - GNUNET_assert (nsh == cls); - if (NULL != zone) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected empty zone but received zone private key\n")); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - res = 1; - return; - } - if ((NULL != label) || (NULL != rd) || (0 != rd_count)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected no zone content but received data\n")); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - res = 1; - return; - } - GNUNET_assert (0); -} - - -static void -empty_zone_end (void *cls) -{ - zi = NULL; - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record 1\n"); - - GNUNET_asprintf (&s_name_1, "dummy1"); - s_rd_1 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_1, - 1, s_rd_1, - &put_cont, - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 2 \n"); - GNUNET_asprintf (&s_name_2, "dummy2"); - s_rd_2 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_2, - 1, s_rd_2, - &put_cont, - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 3\n"); - /* name in different zone */ - GNUNET_asprintf (&s_name_3, "dummy3"); - s_rd_3 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey2, - s_name_3, - 1, - s_rd_3, - &put_cont, - NULL); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - /* first, iterate over empty namestore */ - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &empty_zone_proc, - nsh, - &empty_zone_end, - NULL); - if (NULL == zi) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone iterator\n"); - GNUNET_break (0); - res = 1; - GNUNET_SCHEDULER_shutdown (); - } -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-zone-iteration", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_zone_iteration.c */ diff --git a/src/namestore/test_namestore_api_zone_iteration_nick.c b/src/namestore/test_namestore_api_zone_iteration_nick.c deleted file mode 100644 index c494051d0..000000000 --- a/src/namestore/test_namestore_api_zone_iteration_nick.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_zone_iteration.c - * @brief testcase for zone iteration functionality: iterate all zones - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_gns_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define ZONE_NICK_1 "nick1" -#define ZONE_NICK_2 "nick2" - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PrivateKey privkey2; - -static struct GNUNET_NAMESTORE_ZoneIterator *zi; - -static int res; - -static int returned_records; - -static char *s_name_1; - -static struct GNUNET_GNSRECORD_Data *s_rd_1; - -static char *s_name_2; - -static struct GNUNET_GNSRECORD_Data *s_rd_2; - -static char *s_name_3; - -static struct GNUNET_GNSRECORD_Data *s_rd_3; - -static struct GNUNET_NAMESTORE_QueueEntry *nsqe; - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - * @param tc scheduler context - */ -static void -end (void *cls) -{ - if (NULL != zi) - { - GNUNET_NAMESTORE_zone_iteration_stop (zi); - zi = NULL; - } - if (nsh != NULL) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_free (s_name_1); - GNUNET_free (s_name_2); - GNUNET_free (s_name_3); - - if (s_rd_1 != NULL) - { - GNUNET_free_nz ((void *) s_rd_1->data); - GNUNET_free (s_rd_1); - } - if (s_rd_2 != NULL) - { - GNUNET_free_nz ((void *) s_rd_2->data); - GNUNET_free (s_rd_2); - } - if (s_rd_3 != NULL) - { - GNUNET_free_nz ((void *) s_rd_3->data); - GNUNET_free (s_rd_3); - } -} - - -static int -check_zone_1 (const char *label, unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - for (unsigned int c = 0; c < rd_count; c++) - { - if ((rd[c].record_type == GNUNET_GNSRECORD_TYPE_NICK) && - (0 != strcmp (rd[c].data, ZONE_NICK_1))) - { - GNUNET_break (0); - return GNUNET_YES; - } - } - return GNUNET_NO; -} - - -static int -check_zone_2 (const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - for (unsigned int c = 0; c < rd_count; c++) - { - if ((rd[c].record_type == GNUNET_GNSRECORD_TYPE_NICK) && - (0 != strcmp (rd[c].data, ZONE_NICK_2))) - { - GNUNET_break (0); - return GNUNET_YES; - } - } - return GNUNET_NO; -} - - -static void -zone_proc_end (void *cls) -{ - zi = NULL; - res = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received last result, iteration done after receing %u results\n", - returned_records); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - int failed = GNUNET_NO; - - GNUNET_assert (NULL != zone); - if (0 == GNUNET_memcmp (zone, &privkey)) - { - failed = check_zone_1 (label, rd_count, rd); - if (GNUNET_YES == failed) - GNUNET_break (0); - } - else if (0 == GNUNET_memcmp (zone, &privkey2)) - { - failed = check_zone_2 (label, rd_count, rd); - if (GNUNET_YES == failed) - GNUNET_break (0); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid zone\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - - if (failed == GNUNET_NO) - { - returned_records++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Telling namestore to send the next result\n"); - GNUNET_NAMESTORE_zone_iterator_next (zi, - 1); - } - else - { - GNUNET_break (0); - res = 1; - GNUNET_SCHEDULER_shutdown (); - } -} - - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - static int c = 0; - - if (GNUNET_EC_NONE == ec) - { - c++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record %u \n", c); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to created records: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (c == 3) - { - res = 1; - returned_records = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All records created, starting iteration over all zones \n"); - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &zone_proc, - NULL, - &zone_proc_end, - NULL); - if (zi == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create zone iterator\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - } -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_new_array (count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int c = 0; c < count; c++) - { - rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd[c].record_type = TEST_RECORD_TYPE; - rd[c].data_size = 50; - rd[c].data = GNUNET_malloc (50); - rd[c].flags = 0; - memset ((char *) rd[c].data, 'a', 50); - } - return rd; -} - - -static void -nick_2_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Nick added : %s\n", - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record 1\n"); - - GNUNET_asprintf (&s_name_1, "dummy1"); - s_rd_1 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, &privkey, s_name_1, - 1, s_rd_1, - &put_cont, NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 2 \n"); - GNUNET_asprintf (&s_name_2, "dummy2"); - s_rd_2 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, &privkey, s_name_2, - 1, s_rd_2, &put_cont, NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 3\n"); - - /* name in different zone */ - GNUNET_asprintf (&s_name_3, "dummy3"); - s_rd_3 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, &privkey2, s_name_3, - 1, s_rd_3, - &put_cont, NULL); -} - - -static void -nick_1_cont (void *cls, enum GNUNET_ErrorCode ec) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Nick 1 added : %s\n", - (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); - struct GNUNET_GNSRECORD_Data rd; - - memset (&rd, 0, sizeof(rd)); - rd.data = ZONE_NICK_2; - rd.data_size = strlen (ZONE_NICK_2) + 1; - rd.record_type = GNUNET_GNSRECORD_TYPE_NICK; - rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; - rd.flags |= GNUNET_GNSRECORD_RF_PRIVATE; - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey2, - GNUNET_GNS_EMPTY_LABEL_AT, - 1, - &rd, - &nick_2_cont, - &privkey2); - - if (NULL == nsqe) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore cannot store no block\n")); - } -} - - -/** - * Callback called from the zone iterator when we iterate over - * the empty zone. Check that we got no records and then - * start the actual tests by filling the zone. - */ -static void -empty_zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - GNUNET_assert (nsh == cls); - - if (NULL != zone) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected empty zone but received zone private key\n")); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - if ((NULL != label) || (NULL != rd) || (0 != rd_count)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected no zone content but received data\n")); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_assert (0); -} - - -static void -empty_zone_end (void *cls) -{ - GNUNET_assert (nsh == cls); - struct GNUNET_GNSRECORD_Data rd; - - zi = NULL; - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); - - memset (&rd, 0, sizeof(rd)); - rd.data = ZONE_NICK_1; - rd.data_size = strlen (ZONE_NICK_1) + 1; - rd.record_type = GNUNET_GNSRECORD_TYPE_NICK; - rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; - rd.flags |= GNUNET_GNSRECORD_RF_PRIVATE; - nsqe = GNUNET_NAMESTORE_records_store (nsh, - &privkey, - GNUNET_GNS_EMPTY_LABEL_AT, - 1, - &rd, - &nick_1_cont, - NULL); - if (NULL == nsqe) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Namestore cannot store no block\n")); - } -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - /* first, iterate over empty namestore */ - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &empty_zone_proc, - nsh, - &empty_zone_end, - nsh); - if (NULL == zi) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone iterator\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - } -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-zone-iteration-nick", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_zone_iteration.c */ diff --git a/src/namestore/test_namestore_api_zone_iteration_specific_zone.c b/src/namestore/test_namestore_api_zone_iteration_specific_zone.c deleted file mode 100644 index 02587706c..000000000 --- a/src/namestore/test_namestore_api_zone_iteration_specific_zone.c +++ /dev/null @@ -1,447 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_zone_iteration_specific_zone.c - * @brief testcase for zone iteration functionality: iterate over a specific zone - * @author Matthias Wachs - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PrivateKey privkey2; - -static struct GNUNET_NAMESTORE_ZoneIterator *zi; - -static int res; - -static int returned_records; - -static char *s_name_1; - -static struct GNUNET_GNSRECORD_Data *s_rd_1; - -static char *s_name_2; - -static struct GNUNET_GNSRECORD_Data *s_rd_2; - -static char *s_name_3; - -static struct GNUNET_GNSRECORD_Data *s_rd_3; - - -/** - * Handle timeout. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - endbadly_task = NULL; - GNUNET_SCHEDULER_shutdown (); - res = 1; -} - - -static void -end (void *cls) -{ - if (NULL != zi) - { - GNUNET_NAMESTORE_zone_iteration_stop (zi); - zi = NULL; - } - if (NULL != endbadly_task) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - } - GNUNET_free (s_name_1); - GNUNET_free (s_name_2); - GNUNET_free (s_name_3); - if (s_rd_1 != NULL) - { - GNUNET_free_nz ((void *) s_rd_1->data); - GNUNET_free (s_rd_1); - } - if (s_rd_2 != NULL) - { - GNUNET_free_nz ((void *) s_rd_2->data); - GNUNET_free (s_rd_2); - } - if (s_rd_3 != NULL) - { - GNUNET_free_nz ((void *) s_rd_3->data); - GNUNET_free (s_rd_3); - } - if (nsh != NULL) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } -} - - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); - zi = NULL; -} - - -static void -zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - int failed = GNUNET_NO; - - GNUNET_assert (NULL != zone); - if (0 == GNUNET_memcmp (zone, - &privkey)) - { - if (0 == strcmp (label, s_name_1)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else if (0 == strcmp (label, s_name_2)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid record count\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Comparing result failed: got name `%s' for first zone\n", - label); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else if (0 == GNUNET_memcmp (zone, &privkey2)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received data for not requested zone\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid zone\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - if (failed == GNUNET_NO) - { - returned_records++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Telling namestore to send the next result\n"); - GNUNET_NAMESTORE_zone_iterator_next (zi, - 1); - } - else - { - GNUNET_break (0); - res = 2; - GNUNET_SCHEDULER_shutdown (); - } -} - - -static void -zone_proc_end (void *cls) -{ - zi = NULL; - GNUNET_break (2 == returned_records); - if (2 == returned_records) - { - res = 0; /* Last iteraterator callback, we are done */ - } - else - { - res = 1; - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received last result, iteration done after receing %u results\n", - returned_records); - GNUNET_SCHEDULER_shutdown (); -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - static int c = 0; - - if (GNUNET_EC_NONE == ec) - { - c++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record %u \n", c); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to created records: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - GNUNET_break (0); - res = 2; - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (c == 3) - { - res = 1; - returned_records = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All records created, starting iteration over all zones \n"); - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - &privkey, - &fail_cb, - NULL, - &zone_proc, - NULL, - &zone_proc_end, - NULL); - if (zi == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone iterator\n"); - GNUNET_break (0); - res = 2; - GNUNET_SCHEDULER_shutdown (); - return; - } - } -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_new_array (count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int c = 0; c < count; c++) - { - rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd[c].record_type = TEST_RECORD_TYPE; - rd[c].data_size = 50; - rd[c].data = GNUNET_malloc (50); - rd[c].flags = 0; - memset ((char *) rd[c].data, 'a', 50); - } - return rd; -} - - -/** - * Callback called from the zone iterator when we iterate over - * the empty zone. Check that we got no records and then - * start the actual tests by filling the zone. - */ -static void -empty_zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - GNUNET_assert (nsh == cls); - if (NULL != zone) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected empty zone but received zone private key\n")); - GNUNET_break (0); - res = 2; - GNUNET_SCHEDULER_shutdown (); - return; - } - if ((NULL != label) || (NULL != rd) || (0 != rd_count)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected no zone content but received data\n")); - GNUNET_break (0); - res = 2; - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_assert (0); -} - - -static void -empty_zone_proc_end (void *cls) -{ - zi = NULL; - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 1\n"); - GNUNET_asprintf (&s_name_1, - "dummy1"); - s_rd_1 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_1, - 1, - s_rd_1, - &put_cont, - NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 2 \n"); - GNUNET_asprintf (&s_name_2, - "dummy2"); - s_rd_2 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_2, - 1, - s_rd_2, - &put_cont, - NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 3\n"); - - /* name in different zone */ - GNUNET_asprintf (&s_name_3, - "dummy3"); - s_rd_3 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey2, - s_name_3, - 1, s_rd_3, - &put_cont, - NULL); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - /* first, iterate over empty namestore */ - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &empty_zone_proc, - nsh, - &empty_zone_proc_end, - nsh); - if (NULL == zi) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone iterator\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - } -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ( - "test-namestore-api-zone-iteration-specific-zone", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_zone_iteration_specific_zone.c */ diff --git a/src/namestore/test_namestore_api_zone_iteration_stop.c b/src/namestore/test_namestore_api_zone_iteration_stop.c deleted file mode 100644 index b6b0787ef..000000000 --- a/src/namestore/test_namestore_api_zone_iteration_stop.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_zone_iteration_stop.c - * @brief testcase for zone iteration functionality: stop iterating of zones - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) -#define WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2) - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PrivateKey privkey2; - -static struct GNUNET_NAMESTORE_ZoneIterator *zi; - -static int res; - -static int returned_records; - -static char *s_name_1; - -static struct GNUNET_GNSRECORD_Data *s_rd_1; - -static char *s_name_2; - -static struct GNUNET_GNSRECORD_Data *s_rd_2; - -static char *s_name_3; - -static struct GNUNET_GNSRECORD_Data *s_rd_3; - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -end (void *cls) -{ - if (NULL != zi) - { - GNUNET_NAMESTORE_zone_iteration_stop (zi); - zi = NULL; - } - if (nsh != NULL) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } - GNUNET_free (s_name_1); - GNUNET_free (s_name_2); - GNUNET_free (s_name_3); - if (s_rd_1 != NULL) - { - GNUNET_free_nz ((void *) s_rd_1->data); - GNUNET_free (s_rd_1); - } - if (s_rd_2 != NULL) - { - GNUNET_free_nz ((void *) s_rd_2->data); - GNUNET_free (s_rd_2); - } - if (s_rd_3 != NULL) - { - GNUNET_free_nz ((void *) s_rd_3->data); - GNUNET_free (s_rd_3); - } -} - - -static void -delayed_end (void *cls) -{ - GNUNET_SCHEDULER_shutdown (); -} - - -static void -fail_cb (void *cls) -{ - GNUNET_assert (0); -} - - -static void -zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - int failed = GNUNET_NO; - - GNUNET_assert (NULL != zone); - if (0 == GNUNET_memcmp (zone, &privkey)) - { - if (0 == strcmp (label, s_name_1)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else if (0 == strcmp (label, s_name_2)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid record count\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Comparing result failed: got name `%s' for first zone\n", - label); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else if (0 == GNUNET_memcmp (zone, &privkey2)) - { - if (0 == strcmp (label, s_name_3)) - { - if (rd_count == 1) - { - if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_3)) - { - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid record count\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Comparing result failed: got name `%s' for first zone\n", - label); - failed = GNUNET_YES; - GNUNET_break (0); - } - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Received invalid zone\n"); - failed = GNUNET_YES; - GNUNET_break (0); - } - if (failed == GNUNET_NO) - { - if (1 == returned_records) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Telling namestore to stop zone iteration\n"); - GNUNET_NAMESTORE_zone_iteration_stop (zi); - zi = NULL; - res = 0; - GNUNET_SCHEDULER_add_delayed (WAIT, - &delayed_end, - NULL); - return; - } - returned_records++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Telling namestore to send the next result\n"); - GNUNET_NAMESTORE_zone_iterator_next (zi, - 1); - } - else - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - } -} - - -static void -zone_proc_end (void *cls) -{ - GNUNET_break (1 <= returned_records); - if (1 >= returned_records) - res = 1; /* Last iteraterator callback, we are done */ - else - res = 0; - zi = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received last result, iteration done after receing %u results\n", - returned_records); - GNUNET_SCHEDULER_add_now (&end, NULL); -} - - -static void -put_cont (void *cls, enum GNUNET_ErrorCode ec) -{ - static int c = 0; - - if (GNUNET_EC_NONE == ec) - { - c++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record %u \n", c); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to created records: `%s'\n", - GNUNET_ErrorCode_get_hint (ec)); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (c == 3) - { - res = 1; - returned_records = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All records created, starting iteration over all zones \n"); - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &zone_proc, - NULL, - &zone_proc_end, - NULL); - if (zi == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone iterator\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - } -} - - -static struct GNUNET_GNSRECORD_Data * -create_record (unsigned int count) -{ - struct GNUNET_GNSRECORD_Data *rd; - - rd = GNUNET_new_array (count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int c = 0; c < count; c++) - { - rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_UNIT_HOURS).abs_value_us; - rd[c].record_type = TEST_RECORD_TYPE; - rd[c].data_size = 50; - rd[c].data = GNUNET_malloc (50); - rd[c].flags = 0; - memset ((char *) rd[c].data, 'a', 50); - } - return rd; -} - - -/** - * Callback called from the zone iterator when we iterate over - * the empty zone. Check that we got no records and then - * start the actual tests by filling the zone. - */ -static void -empty_zone_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - GNUNET_assert (nsh == cls); - if (NULL != zone) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected empty zone but received zone private key\n")); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - if ((NULL != label) || (NULL != rd) || (0 != rd_count)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Expected no zone content but received data\n")); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_assert (0); -} - - -static void -empty_zone_proc_end (void *cls) -{ - GNUNET_assert (nsh == cls); - zi = NULL; - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 1\n"); - - GNUNET_asprintf (&s_name_1, - "dummy1"); - s_rd_1 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey, s_name_1, - 1, s_rd_1, &put_cont, NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 2 \n"); - GNUNET_asprintf (&s_name_2, - "dummy2"); - s_rd_2 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name_2, - 1, - s_rd_2, - &put_cont, NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created record 3\n"); - - /* name in different zone */ - GNUNET_asprintf (&s_name_3, "dummy3"); - s_rd_3 = create_record (1); - GNUNET_NAMESTORE_records_store (nsh, - &privkey2, - s_name_3, - 1, - s_rd_3, - &put_cont, NULL); -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - /* first, iterate over empty namestore */ - zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, - NULL, - &fail_cb, - NULL, - &empty_zone_proc, - nsh, - &empty_zone_proc_end, - nsh); - if (NULL == zi) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create zone iterator\n"); - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - } -} - - -#include "test_common.c" - - -int -main (int argc, char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-zone-iteration-stop", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - - return res; -} - - -/* end of test_namestore_api_zone_iteration_stop.c */ diff --git a/src/namestore/test_namestore_api_zone_to_name.c b/src/namestore/test_namestore_api_zone_to_name.c deleted file mode 100644 index c70eef53a..000000000 --- a/src/namestore/test_namestore_api_zone_to_name.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file namestore/test_namestore_api_zone_to_name.c - * @brief testcase for zone to name translation - */ -#include "platform.h" -#include "gnunet_namestore_service.h" -#include "gnunet_testing_lib.h" -#include "namestore.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -#define RECORDS 5 - -#define TEST_RECORD_DATALEN 123 - -#define TEST_RECORD_DATA 'a' - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) - - -static struct GNUNET_NAMESTORE_Handle *nsh; - -static struct GNUNET_SCHEDULER_Task *endbadly_task; - -static struct GNUNET_CRYPTO_PrivateKey privkey; - -static struct GNUNET_CRYPTO_PublicKey pubkey; - -static struct GNUNET_CRYPTO_PublicKey s_zone_value; - -static char *s_name; - -static int res; - -static struct GNUNET_NAMESTORE_QueueEntry *qe; - - -/** - * Re-establish the connection to the service. - * - * @param cls handle to use to re-connect. - */ -static void -endbadly (void *cls) -{ - (void) cls; - GNUNET_SCHEDULER_shutdown (); - res = 1; -} - - -static void -end (void *cls) -{ - if (NULL != qe) - { - GNUNET_NAMESTORE_cancel (qe); - qe = NULL; - } - if (NULL != endbadly_task) - { - GNUNET_SCHEDULER_cancel (endbadly_task); - endbadly_task = NULL; - } - if (NULL != nsh) - { - GNUNET_NAMESTORE_disconnect (nsh); - nsh = NULL; - } -} - - -static void -zone_to_name_proc (void *cls, - const struct GNUNET_CRYPTO_PrivateKey *zone_key, - const char *n, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - int fail = GNUNET_NO; - - qe = NULL; - if ((NULL == zone_key) && - (NULL == n) && - (0 == rd_count) && - (NULL == rd)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No result found\n"); - res = 1; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Result found: `%s'\n", - n); - if ((NULL == n) || - (0 != strcmp (n, - s_name))) - { - fail = GNUNET_YES; - GNUNET_break (0); - } - if (1 != rd_count) - { - fail = GNUNET_YES; - GNUNET_break (0); - } - if ((NULL == zone_key) || - (0 != GNUNET_memcmp (zone_key, - &privkey))) - { - fail = GNUNET_YES; - GNUNET_break (0); - } - if (fail == GNUNET_NO) - res = 0; - else - res = 1; - } - GNUNET_SCHEDULER_add_now (&end, - NULL); -} - - -static void -error_cb (void *cls) -{ - (void) cls; - qe = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Not found!\n"); - GNUNET_SCHEDULER_shutdown (); - res = 2; -} - - -static void -put_cont (void *cls, - enum GNUNET_ErrorCode ec) -{ - char *name = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Name store added record for `%s': %s\n", - name, - (GNUNET_EC_NONE == ec) ? - "SUCCESS" : GNUNET_ErrorCode_get_hint (ec)); - if (GNUNET_EC_NONE == ec) - { - res = 0; - - qe = GNUNET_NAMESTORE_zone_to_name (nsh, - &privkey, - &s_zone_value, - &error_cb, - NULL, - &zone_to_name_proc, - NULL); - } - else - { - res = 1; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to put records for name `%s'\n", - name); - GNUNET_SCHEDULER_add_now (&end, - NULL); - } -} - - -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - (void) cls; - (void) peer; - endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, - &endbadly, - NULL); - GNUNET_SCHEDULER_add_shutdown (&end, - NULL); - GNUNET_asprintf (&s_name, "dummy"); - privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); - /* get public key */ - GNUNET_CRYPTO_key_get_public (&privkey, - &pubkey); - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - &s_zone_value, - sizeof(s_zone_value)); - s_zone_value.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); - { - struct GNUNET_GNSRECORD_Data rd; - - rd.expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; - rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY; - rd.data_size = sizeof (s_zone_value.ecdsa_key); - rd.data = &s_zone_value.ecdsa_key; - rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - - nsh = GNUNET_NAMESTORE_connect (cfg); - GNUNET_break (NULL != nsh); - GNUNET_NAMESTORE_records_store (nsh, - &privkey, - s_name, - 1, - &rd, - &put_cont, - s_name); - } -} - - -#include "test_common.c" - - -int -main (int argc, - char *argv[]) -{ - const char *plugin_name; - char *cfg_name; - - (void) argc; - SETUP_CFG (plugin_name, cfg_name); - res = 1; - if (0 != - GNUNET_TESTING_peer_run ("test-namestore-api-zone-to-name", - cfg_name, - &run, - NULL)) - { - res = 1; - } - GNUNET_DISK_purge_cfg_dir (cfg_name, - "GNUNET_TEST_HOME"); - GNUNET_free (plugin_name); - GNUNET_free (cfg_name); - return res; -} - - -/* end of test_namestore_api_zone_to_name.c */ diff --git a/src/namestore/test_namestore_delete.sh b/src/namestore/test_namestore_delete.sh deleted file mode 100755 index b861a4bc0..000000000 --- a/src/namestore/test_namestore_delete.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash -CONFIGURATION="test_namestore_api.conf" -trap "gnunet-arm -e -c $CONFIGURATION" SIGINT - -LOCATION=$(which gnunet-config) -if [ -z $LOCATION ] -then - LOCATION="gnunet-config" -fi -$LOCATION --version 1> /dev/null -if test $? != 0 -then - echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" - exit 77 -fi - -rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` -TEST_DOMAIN_PLUS="www.gnu" -TEST_DOMAIN_DNS="www3.gnu" -TEST_IP_PLUS="127.0.0.1" -TEST_IP_DNS="131.159.74.67" -TEST_RECORD_CNAME_SERVER="server" -TEST_RECORD_CNAME_PLUS="server.+" -TEST_RECORD_CNAME_DNS="gnunet.org" -TEST_RECORD_NAME_SERVER="server" -TEST_RECORD_NAME_PLUS="www" -TEST_RECORD_NAME_DNS="www3" -which timeout &> /dev/null && DO_TIMEOUT="timeout 5" - -function start_peer -{ - gnunet-arm -s -c $CONFIGURATION - gnunet-identity -C testego -c $CONFIGURATION -} - -function stop_peer -{ - gnunet-identity -D testego -c $CONFIGURATION - gnunet-arm -e -c $CONFIGURATION -} - - -start_peer -# Create a public record -gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION -# Delete record -gnunet-namestore -p -z testego -d -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION -# List all records -OUTPUT=`gnunet-namestore -p -z testego -D` -FOUND_IP=false -FOUND_NAME=false -for LINE in $OUTPUT ; - do - if echo "$LINE" | grep -q "$TEST_RECORD_NAME_DNS"; then - FOUND_NAME=true; - fi - if echo "$LINE" | grep -q "$TEST_IP_PLUS"; then - FOUND_IP=true; - fi - done -stop_peer - - -if [ $FOUND_IP = true ] -then - echo "FAIL: Delete name in namestore: IP returned" - exit 1 -fi diff --git a/src/namestore/test_namestore_lookup.sh b/src/namestore/test_namestore_lookup.sh deleted file mode 100755 index 1c96e102a..000000000 --- a/src/namestore/test_namestore_lookup.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash -CONFIGURATION="test_namestore_api.conf" -trap "gnunet-arm -e -c $CONFIGURATION" SIGINT - -LOCATION=$(which gnunet-config) -if [ -z $LOCATION ] -then - LOCATION="gnunet-config" -fi -$LOCATION --version 1> /dev/null -if test $? != 0 -then - echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" - exit 77 -fi - -rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` -TEST_IP_PLUS="127.0.0.1" -TEST_RECORD_NAME_DNS="www3" -which timeout &> /dev/null && DO_TIMEOUT="timeout 5" - -# start peer -gnunet-arm -s -c $CONFIGURATION -gnunet-identity -C testego -c $CONFIGURATION - -# Create a public record -gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION -NAMESTORE_RES=$? -# Lookup specific name -OUTPUT=`gnunet-namestore -p -z testego -n $TEST_RECORD_NAME_DNS -D` - - -FOUND_IP=false -FOUND_NAME=false -for LINE in $OUTPUT ; - do - if echo "$LINE" | grep -q "$TEST_RECORD_NAME_DNS"; then - FOUND_NAME=true; - #echo $FOUND_NAME - fi - if echo "$LINE" | grep -q "$TEST_IP_PLUS"; then - FOUND_IP=true; - #echo $FOUND_IP - fi -done -# stop peer -gnunet-identity -D testego -c $CONFIGURATION -gnunet-arm -e -c $CONFIGURATION - - -if [ $FOUND_NAME = true -a $FOUND_IP = true ] -then - echo "PASS: Lookup name in namestore" - exit 0 -elif [ $FOUND_NAME = false ] -then - echo "FAIL: Lookup name in namestore: name not returned" - exit 1 -elif [ $FOUND_IP = false ] -then - echo "FAIL: Lookup name in namestore: IP not returned" - exit 1 -fi diff --git a/src/namestore/test_namestore_put.sh b/src/namestore/test_namestore_put.sh deleted file mode 100755 index eaf7d44b4..000000000 --- a/src/namestore/test_namestore_put.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -CONFIGURATION="test_namestore_api.conf" -trap "gnunet-arm -e -c $CONFIGURATION" SIGINT - -LOCATION=$(which gnunet-config) -if [ -z $LOCATION ] -then - LOCATION="gnunet-config" -fi -$LOCATION --version 1> /dev/null -if test $? != 0 -then - echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" - exit 77 -fi - -rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` -TEST_DOMAIN_PLUS="www.gnu" -TEST_DOMAIN_DNS="www3.gnu" -TEST_IP_PLUS="127.0.0.1" -TEST_IP_DNS="131.159.74.67" -TEST_RECORD_CNAME_SERVER="server" -TEST_RECORD_CNAME_PLUS="server.+" -TEST_RECORD_CNAME_DNS="gnunet.org" -TEST_RECORD_NAME_SERVER="server" -TEST_RECORD_NAME_PLUS="www" -TEST_RECORD_NAME_DNS="www3" -which timeout &> /dev/null && DO_TIMEOUT="timeout 5" - -function start_peer -{ - gnunet-arm -s -c $CONFIGURATION - gnunet-identity -C testego -c $CONFIGURATION -} - -function stop_peer -{ - gnunet-identity -D testego -c $CONFIGURATION - gnunet-arm -e -c $CONFIGURATION -} - - -start_peer -# Create a public record -gnunet-namestore -p -z testego -a -n $TEST_RECORD_NAME_DNS -t A -V $TEST_IP_PLUS -e never -c $CONFIGURATION -NAMESTORE_RES=$? -stop_peer - -if [ $NAMESTORE_RES = 0 ] -then - echo "PASS: Creating name in namestore" -else - echo "FAIL: Creating name in namestore failed with $NAMESTORE_RES." - exit 1 -fi diff --git a/src/namestore/test_namestore_put_multiple.sh b/src/namestore/test_namestore_put_multiple.sh deleted file mode 100755 index 4c7340440..000000000 --- a/src/namestore/test_namestore_put_multiple.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/bash - -# Check for required packages -if ! [ -x "$(command -v gnunet-namestore)" ]; then - echo 'bind/named is not installed' >&2 - exit 1 -fi - -# Check if gnunet is running -gnunet-arm -I 2&>1 /dev/null -ret=$? -if [ 0 -ne $ret ]; then - echo 'gnunet services are not running' - exit 1 -fi - -## GNUNET part -# Check if identity exists and deletes and readds it to get rid of entries in zone -gnunet-identity -d | grep randomtestingid 2>&1 /dev/null -ret=$? - -if [ 0 -ne $ret ]; then - gnunet-identity -D randomtestingid - gnunet-identity -C randomtestingid -fi - -function get_record_type { - arr=$1 - typ=$(echo -n "${arr[0]}" | cut -d' ' -f1) - echo "$typ" -} - -function get_value { - arr=$1 - val=$(echo -n "${arr[0]}" | cut -d' ' -f4-) - echo "$val" -} - -function testing { - label=$1 - records=$2 - recordstring="" - typ=$(get_record_type "${records[@]}") - for i in "${records[@]}" - do - recordstring+="$i"$'\n' - done - echo "$recordstring" - gnunet-namestore -a -S <&1 /dev/null - if [ 0 -ne $ret ]; then - echo "record $label could not be found" - fi -} - -# TEST CASES -# 1 -echo "Testing adding of single A record with -R" -declare -a arr=('A 1200 [r] 127.0.0.1') -testing test1 "${arr[@]}" -# 2 -echo "Testing adding of multiple A records with -R" -declare -a arr=('A 1200 [r] 127.0.0.1' 'A 2400 [r] 127.0.0.2') -testing test2 "${arr[@]}" -# 3 -echo "Testing adding of multiple different records with -R" -declare -a arr=('A 1200 [r] 127.0.0.1' 'AAAA 2400 [r] 2002::') -testing test3 "${arr[@]}" -# 4 -echo "Testing adding of single GNS2DNS record with -R" -declare -a arr=('GNS2DNS 86400 [r] gnu.org@127.0.0.1') -testing test4 "${arr[@]}" -# 5 -echo "Testing adding of single GNS2DNS shadow record with -R" -declare -a arr=('GNS2DNS 86409 [rs] gnu.org@127.0.0.250') -testing test5 "${arr[@]}" -# 6 -echo "Testing adding of multiple GNS2DNS record with -R" -declare -a arr=('GNS2DNS 1 [r] gnunet.org@127.0.0.1' 'GNS2DNS 3600 [s] gnunet.org@127.0.0.2') -testing test6 "${arr[@]}" -val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid) -if [[ $val == *"127.0.0.1"* ]]; then - echo "shadow!" -fi -echo "Sleeping to let record expire" -sleep 5 -val=$(gnunet-gns -t GNS2DNS -u test6.randomtestingid) -if [[ $val == *"127.0.0.2"* ]]; then - echo "no shadow!" -fi -# 7 -echo "Testing adding MX record with -R" -declare -a arr=('MX 3600 [r] 10,mail') -testing test7 "${arr[@]}" -# 8 -echo "Testing adding TXT record with -R" -declare -a arr=('TXT 3600 [r] Pretty_Unicorns') -testing test8 "${arr[@]}" -# 8 -#echo "Testing adding TXT record with -R" -#declare -a arr=('SRV 3600 [r] _autodiscover_old._tcp.bfh.ch.') -#testing test8 "${arr[@]}" - -# CLEANUP -gnunet-identity -D randomtestingid diff --git a/src/namestore/test_namestore_put_stdin.sh b/src/namestore/test_namestore_put_stdin.sh deleted file mode 100755 index deb7bc4cb..000000000 --- a/src/namestore/test_namestore_put_stdin.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash -CONFIGURATION="test_namestore_api.conf" -trap "gnunet-arm -e -c $CONFIGURATION" SIGINT - -LOCATION=$(which gnunet-config) -if [ -z $LOCATION ] -then - LOCATION="gnunet-config" -fi -$LOCATION --version 1> /dev/null -if test $? != 0 -then - echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" - exit 77 -fi - -rm -rf `$LOCATION -c $CONFIGURATION -s PATHS -o GNUNET_HOME` -TEST_RECORD_NAME="www3" -TEST_RECORD_NAME2="www" -TEST_IP="8.7.6.5" -TEST_IP2="1.2.3.4" - -which timeout &> /dev/null && DO_TIMEOUT="timeout 5" - -function start_peer -{ - gnunet-arm -s -c $CONFIGURATION - gnunet-identity -C testego -c $CONFIGURATION - gnunet-identity -C testego2 -c $CONFIGURATION -} - -function stop_peer -{ - gnunet-identity -D testego -c $CONFIGURATION - gnunet-identity -D testego2 -c $CONFIGURATION - gnunet-arm -e -c $CONFIGURATION -} - - -start_peer -# Create a public record -EGOKEY=`gnunet-identity -d | grep testego2 | cut -d' ' -f3` -gnunet-namestore -a -c $CONFIGURATION -S < /dev/null -if test $? != 0 -then - echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" - exit 77 -fi - -rm -rf `gnunet-config -c test_namestore_api.conf -f -s paths -o GNUNET_TEST_HOME` -which timeout > /dev/null 2>&1 && DO_TIMEOUT="timeout 5" - -MY_EGO="myego" -gnunet-arm -s -c test_namestore_api.conf -gnunet-identity -C $MY_EGO -c test_namestore_api.conf -gnunet-namestore-zonefile -c test_namestore_api.conf < example_zonefile -res=$? -gnunet-identity -D $MY_EGO -c test_namestore_api.conf -gnunet-arm -e -c test_namestore_api.conf - -if [ $res != 0 ]; then - echo "FAIL: Zone import failed." - exit 1 -fi - - diff --git a/src/namestore/test_plugin_namestore.c b/src/namestore/test_plugin_namestore.c deleted file mode 100644 index 388b23f57..000000000 --- a/src/namestore/test_plugin_namestore.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/* - * @file namestore/test_plugin_namestore.c - * @brief Test for the namestore plugins - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_namestore_plugin.h" -#include "gnunet_testing_lib.h" - -#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT - -static int ok; - -/** - * Name of plugin under test. - */ -static const char *plugin_name; - - -/** - * Function called when the service shuts down. Unloads our namestore - * plugin. - * - * @param api api to unload - */ -static void -unload_plugin (struct GNUNET_NAMESTORE_PluginFunctions *api) -{ - char *libname; - - GNUNET_asprintf (&libname, "libgnunet_plugin_namestore_%s", plugin_name); - GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api)); - GNUNET_free (libname); -} - - -/** - * Load the namestore plugin. - * - * @param cfg configuration to pass - * @return NULL on error - */ -static struct GNUNET_NAMESTORE_PluginFunctions * -load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_NAMESTORE_PluginFunctions *ret; - char *libname; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Loading `%s' namestore plugin\n", - plugin_name); - GNUNET_asprintf (&libname, - "libgnunet_plugin_namestore_%s", - plugin_name); - if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void *) cfg))) - { - fprintf (stderr, - "Failed to load plugin `%s'!\n", - plugin_name); - GNUNET_free (libname); - return NULL; - } - GNUNET_free (libname); - if (GNUNET_OK != ret->drop_tables (ret->cls)) - { - GNUNET_break (0); - return NULL; - } - if (GNUNET_OK != ret->create_tables (ret->cls)) - { - GNUNET_break (0); - return NULL; - } - return ret; -} - - -static void -test_record (void *cls, - uint64_t seq, - const struct GNUNET_CRYPTO_PrivateKey *private_key, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - int *idp = cls; - int id = *idp; - struct GNUNET_CRYPTO_PrivateKey tzone_private_key; - char tname[64]; - unsigned int trd_count = 1 + (id % 1024); - - GNUNET_snprintf (tname, sizeof(tname), "a%u", (unsigned int) id); - GNUNET_assert (trd_count == rd_count); - for (unsigned int i = 0; i < trd_count; i++) - { - GNUNET_assert (rd[i].data_size == id % 10); - GNUNET_assert (0 == memcmp ("Hello World", rd[i].data, id % 10)); - GNUNET_assert (rd[i].record_type == TEST_RECORD_TYPE); - GNUNET_assert (rd[i].flags == 0); - } - memset (&tzone_private_key, (id % 241), sizeof(tzone_private_key)); - GNUNET_assert (0 == strcmp (label, tname)); - GNUNET_assert (0 == GNUNET_memcmp (&tzone_private_key, private_key)); -} - - -static void -get_record (struct GNUNET_NAMESTORE_PluginFunctions *nsp, int id) -{ - GNUNET_assert ( - GNUNET_OK == - nsp->iterate_records (nsp->cls, NULL, 0, 1, &test_record, &id)); -} - - -static void -put_record (struct GNUNET_NAMESTORE_PluginFunctions *nsp, int id) -{ - struct GNUNET_CRYPTO_PrivateKey zone_private_key; - char label[64]; - unsigned int rd_count = 1 + (id % 1024); - struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; - struct GNUNET_CRYPTO_EcdsaSignature signature; - - GNUNET_snprintf (label, sizeof(label), "a%u", (unsigned int) id); - for (unsigned int i = 0; i < rd_count; i++) - { - rd[i].data = "Hello World"; - rd[i].data_size = id % 10; - rd[i].expiration_time = - GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES).abs_value_us; - rd[i].record_type = TEST_RECORD_TYPE; - rd[i].flags = 0; - } - memset (&zone_private_key, (id % 241), sizeof(zone_private_key)); - memset (&signature, (id % 243), sizeof(signature)); - GNUNET_assert ( - GNUNET_OK == - nsp->store_records (nsp->cls, &zone_private_key, label, rd_count, rd)); -} - - -static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_NAMESTORE_PluginFunctions *nsp; - - ok = 0; - nsp = load_plugin (cfg); - if (NULL == nsp) - { - fprintf ( - stderr, - "%s", - "Failed to initialize namestore. Database likely not setup, skipping test.\n"); - return; - } - put_record (nsp, 1); - get_record (nsp, 1); -#ifndef DARWIN // #5582 - unload_plugin (nsp); -#endif -} - - -int -main (int argc, char *argv[]) -{ - char cfg_name[PATH_MAX]; - char *const xargv[] = { "test-plugin-namestore", "-c", cfg_name, NULL }; - struct GNUNET_GETOPT_CommandLineOption options[] = - { GNUNET_GETOPT_OPTION_END }; - - GNUNET_log_setup ("test-plugin-namestore", "WARNING", NULL); - plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); - GNUNET_snprintf (cfg_name, - sizeof(cfg_name), - "test_plugin_namestore_%s.conf", - plugin_name); - GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TMP"); - GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, - xargv, - "test-plugin-namestore", - "nohelp", - options, - &run, - NULL); - GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TMP"); - if (ok != 0) - fprintf (stderr, "Missed some testcases: %d\n", ok); - return ok; -} - - -/* end of test_plugin_namestore.c */ diff --git a/src/namestore/test_plugin_namestore_postgres.conf b/src/namestore/test_plugin_namestore_postgres.conf deleted file mode 100644 index 140d54623..000000000 --- a/src/namestore/test_plugin_namestore_postgres.conf +++ /dev/null @@ -1,4 +0,0 @@ -[namestore-postgres] -CONFIG = connect_timeout=10 dbname=gnunetcheck -INIT_ON_CONNECT = YES -#TEMPORARY_TABLE = YES diff --git a/src/namestore/test_plugin_namestore_sqlite.conf b/src/namestore/test_plugin_namestore_sqlite.conf deleted file mode 100644 index 365198db2..000000000 --- a/src/namestore/test_plugin_namestore_sqlite.conf +++ /dev/null @@ -1,3 +0,0 @@ -[namestore-sqlite] -FILENAME = $GNUNET_TMP/gnunet-test-plugin-namestore-sqlite/sqlite.db -INIT_ON_CONNECT = YES diff --git a/src/namestore/test_plugin_rest_namestore.sh b/src/namestore/test_plugin_rest_namestore.sh deleted file mode 100755 index 4f117db8b..000000000 --- a/src/namestore/test_plugin_rest_namestore.sh +++ /dev/null @@ -1,131 +0,0 @@ -#!/bin/sh -trap "gnunet-arm -e -c test_gns_lookup.conf" SIGINT - -LOCATION=$(which gnunet-config) -if [ -z $LOCATION ] -then - LOCATION="gnunet-config" -fi -$LOCATION --version 1> /dev/null -if test $? != 0 -then - echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" - exit 77 -fi - -rm -rf `gnunet-config -c test_namestore_api.conf -f -s paths -o GNUNET_TEST_HOME` - -namestore_link="http://localhost:7776/namestore" -wrong_link="http://localhost:7776/namestoreandmore" - -curl_get () { - #$1 is link - #$2 is grep - resp=$(curl -v "$1" 2>&1) - cache="$(echo $resp | grep "$2")" - #echo $cache - if [ "" = "$cache" ] - then - echo "Error in get response: $resp, expected $2" - gnunet-arm -e -c test_namestore_api.conf - exit 1 - fi -} - -curl_post () { - #$1 is link - #$2 is data - #$3 is grep - resp=$(curl -v -X "POST" "$1" --data "$2" 2>&1) - cache="$(echo $resp | grep "$3")" - #echo $cache - if [ "" = "$cache" ] - then - echo "Error in post response: $resp ($2), expected $3" - gnunet-arm -e -c test_namestore_api.conf - exit 1 - fi -} - -curl_delete () { - #$1 is link - #$2 is grep - resp=$(curl -v -X "DELETE" "$1" 2>&1) - cache="$(echo $resp | grep "$2")" - #echo $cache - if [ "" = "$cache" ] - then - echo "Error in delete response: $resp, expected $2" - gnunet-arm -e -c test_namestore_api.conf - exit 1 - fi -} - -# curl_put () { -# #$1 is link -# #$2 is data -# #$3 is grep -# cache="$(curl -v -X "PUT" "$1" --data "$2" 2>&1 | grep "$3")" -# #echo $cache -# if [ "" == "$cache" ] -# then -# exit 1 -# fi -# } - -#Test subsystem default identity - -TEST_ID="test" -gnunet-arm -s -c test_namestore_api.conf -#Test GET -gnunet-identity -C $TEST_ID -c test_namestore_api.conf -test="$(gnunet-namestore -D -z $TEST_ID -c test_namestore_api.conf)" -name=$TEST_ID -public="$(gnunet-identity -d -c test_namestore_api.conf | grep $TEST_ID | awk 'NR==1{print $3}')" -echo "$name $public" -gnunet-namestore -z $name -p -a -n "test_entry" -e "1d" -V "000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8" -t "PKEY" -c test_namestore_api.conf -sleep 1 -gnunet-arm -i rest -c test_namestore_api.conf -sleep 1 -curl_get "${namestore_link}/$name" "HTTP/1.1 200 OK" -curl_get "${namestore_link}/$public" "error" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf - -#Test POST with NAME -curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration": 86400000000, "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "HTTP/1.1 204 No Content" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - -# invalid values -curl_post "${namestore_link}/$name" '{"data": [{"value":"HVX38H2CB7WJM0WCPWT9CFX6GASMYJVR65RN75SJSSKAYVYXHMRGxxx", "record_type":"PKEY", "relative_expiration": 86400000000, "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "error" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - - -curl_post "${namestore_link}/$name" '{"data": [{"value":"", "record_type":"PKEY", "relative_expiration": 86400000000,"flag":0,"record_name"}]:"test_entry"}' "error" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - -curl_post "${namestore_link}/$name" '{"data": [{"record_type":"PKEY", "relative_expiration": 86400000000,"flag":0}],"record_name":"test_entry"}' "error" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - -#expirations -curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":0, "is_relative_expiration": true, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "HTTP/1.1 204" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - -curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":864000000000000, "is_relative_expiration": true, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "HTTP/1.1 204" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - -curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "expiration_time_missing":"1d", "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "error" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - -#record_name -curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":86400000000, "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":""}' "error" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 -curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":"1d", "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name_missing":"test_entry"}' "error" -gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 - -#Test DELETE -gnunet-namestore -z $name -p -a -n "test_entry" -e "1d" -V "000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8" -t "PKEY" -c test_namestore_api.conf -curl_delete "${namestore_link}/$name/test_entry" "HTTP/1.1 204" - -gnunet-arm -e -c test_namestore_api.conf -exit 0; - diff --git a/src/plugin/Makefile.am b/src/plugin/Makefile.am index 2939d3379..62036f206 100644 --- a/src/plugin/Makefile.am +++ b/src/plugin/Makefile.am @@ -2,4 +2,5 @@ SUBDIRS = \ block \ gnsrecord \ dhtu \ - namecache + namecache \ + namestore diff --git a/src/plugin/namestore/Makefile.am b/src/plugin/namestore/Makefile.am new file mode 100644 index 000000000..3c8e9d170 --- /dev/null +++ b/src/plugin/namestore/Makefile.am @@ -0,0 +1,439 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS) + +plugindir = $(libdir)/gnunet + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +sqldir = $(prefix)/share/gnunet/sql/ + +sql_DATA = \ + namestore-0001.sql \ + namestore-drop.sql + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIBS = -lgcov +endif + + +if HAVE_SQLITE +SQLITE_PLUGIN = libgnunet_plugin_namestore_sqlite.la +SQLITE_TESTS = test_plugin_namestore_sqlite \ + test_namestore_api_store_sqlite \ + test_namestore_api_store_update_sqlite \ + test_namestore_api_zone_iteration_sqlite \ + test_namestore_api_remove_sqlite \ + test_namestore_api_lookup_nick_sqlite \ + test_namestore_api_monitoring_sqlite \ + test_namestore_api_remove_not_existing_record_sqlite \ + test_namestore_api_zone_iteration_nick_sqlite \ + test_namestore_api_zone_iteration_specific_zone_sqlite \ + test_namestore_api_zone_iteration_stop_sqlite \ + test_namestore_api_monitoring_existing_sqlite \ + test_namestore_api_zone_to_name_sqlite \ + perf_namestore_api_zone_iteration_sqlite \ + perf_namestore_api_import_sqlite \ + perf_namestore_api_import_postgres \ + test_namestore_api_tx_rollback_sqlite +endif + + +if HAVE_POSTGRESQL +POSTGRES_PLUGIN = libgnunet_plugin_namestore_postgres.la +POSTGRES_TESTS = test_plugin_namestore_postgres \ + test_namestore_api_store_postgres \ + test_namestore_api_store_update_postgres \ + test_namestore_api_remove_postgres \ + test_namestore_api_zone_iteration_postgres \ + test_namestore_api_lookup_nick_postgres \ + test_namestore_api_monitoring_postgres \ + test_namestore_api_remove_not_existing_record_postgres \ + test_namestore_api_zone_iteration_nick_postgres \ + test_namestore_api_zone_iteration_specific_zone_postgres \ + test_namestore_api_zone_iteration_stop_postgres \ + test_namestore_api_monitoring_existing_postgres \ + test_namestore_api_zone_to_name_postgres \ + perf_namestore_api_zone_iteration_postgres \ + test_namestore_api_tx_rollback_postgres +if HAVE_EXPERIMENTAL +POSTGRES_TESTS += test_namestore_api_edit_records_postgres +endif +endif + +if HAVE_SQLITE +check_PROGRAMS = \ + $(SQLITE_TESTS) \ + $(POSTGRES_TESTS) +endif + +if ENABLE_TEST_RUN +AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; +TESTS = \ + $(check_PROGRAMS) \ + $(check_SCRIPTS) +endif + +REST_PLUGIN = libgnunet_plugin_rest_namestore.la + +plugin_LTLIBRARIES = \ + $(SQLITE_PLUGIN) \ + $(POSTGRES_PLUGIN) \ + $(REST_PLUGIN) + + +libgnunet_plugin_rest_namestore_la_SOURCES = \ + plugin_rest_namestore.c +libgnunet_plugin_rest_namestore_la_LIBADD = \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/rest/libgnunetrest.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/json/libgnunetjson.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecordjson.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ + $(LTLIBINTL) -ljansson $(MHD_LIBS) +libgnunet_plugin_rest_namestore_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) +libgnunet_plugin_rest_namestore_la_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) + + +libgnunet_plugin_namestore_sqlite_la_SOURCES = \ + plugin_namestore_sqlite.c +libgnunet_plugin_namestore_sqlite_la_LIBADD = \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/sq/libgnunetsq.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ + $(LTLIBINTL) +libgnunet_plugin_namestore_sqlite_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_plugin_namestore_postgres_la_SOURCES = \ + plugin_namestore_postgres.c +libgnunet_plugin_namestore_postgres_la_LIBADD = \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/pq/libgnunetpq.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) -lpq \ + $(LTLIBINTL) +libgnunet_plugin_namestore_postgres_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS) + +test_namestore_api_store_sqlite_SOURCES = \ + test_namestore_api_store.c +test_namestore_api_store_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_store_postgres_SOURCES = \ + test_namestore_api_store.c +test_namestore_api_store_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_store_update_sqlite_SOURCES = \ + test_namestore_api_store_update.c +test_namestore_api_store_update_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_store_update_postgres_SOURCES = \ + test_namestore_api_store_update.c +test_namestore_api_store_update_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_lookup_nick_sqlite_SOURCES = \ + test_namestore_api_lookup_nick.c +test_namestore_api_lookup_nick_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_lookup_nick_postgres_SOURCES = \ + test_namestore_api_lookup_nick.c +test_namestore_api_lookup_nick_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_remove_sqlite_SOURCES = \ + test_namestore_api_remove.c +test_namestore_api_remove_sqlite_LDADD = \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_remove_postgres_SOURCES = \ + test_namestore_api_remove.c +test_namestore_api_remove_postgres_LDADD = \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_remove_not_existing_record_sqlite_SOURCES = \ + test_namestore_api_remove_not_existing_record.c +test_namestore_api_remove_not_existing_record_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_remove_not_existing_record_postgres_SOURCES = \ + test_namestore_api_remove_not_existing_record.c +test_namestore_api_remove_not_existing_record_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_to_name_sqlite_SOURCES = \ + test_namestore_api_zone_to_name.c +test_namestore_api_zone_to_name_sqlite_LDADD = \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_to_name_postgres_SOURCES = \ + test_namestore_api_zone_to_name.c +test_namestore_api_zone_to_name_postgres_LDADD = \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_monitoring_sqlite_SOURCES = \ + test_namestore_api_monitoring.c +test_namestore_api_monitoring_sqlite_LDADD = \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_namestore_api_monitoring_postgres_SOURCES = \ + test_namestore_api_monitoring.c +test_namestore_api_monitoring_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_namestore_api_monitoring_existing_sqlite_SOURCES = \ + test_namestore_api_monitoring_existing.c +test_namestore_api_monitoring_existing_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_namestore_api_monitoring_existing_postgres_SOURCES = \ + test_namestore_api_monitoring_existing.c +test_namestore_api_monitoring_existing_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_namestore_api_tx_rollback_sqlite_SOURCES = \ + test_namestore_api_tx_rollback.c +test_namestore_api_tx_rollback_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_namestore_api_tx_rollback_postgres_SOURCES = \ + test_namestore_api_tx_rollback.c +test_namestore_api_tx_rollback_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +if HAVE_EXPERIMENTAL +test_namestore_api_edit_records_postgres_SOURCES = \ + test_namestore_api_edit_records.c +test_namestore_api_edit_records_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la +endif + +test_namestore_api_zone_iteration_sqlite_SOURCES = \ + test_namestore_api_zone_iteration.c +test_namestore_api_zone_iteration_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_iteration_postgres_SOURCES = \ + test_namestore_api_zone_iteration.c +test_namestore_api_zone_iteration_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +perf_namestore_api_zone_iteration_postgres_SOURCES = \ + perf_namestore_api_zone_iteration.c +perf_namestore_api_zone_iteration_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +perf_namestore_api_import_sqlite_SOURCES = \ + perf_namestore_api_import.c +perf_namestore_api_import_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +perf_namestore_api_import_postgres_SOURCES = \ + perf_namestore_api_import.c +perf_namestore_api_import_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + + +perf_namestore_api_zone_iteration_sqlite_SOURCES = \ + perf_namestore_api_zone_iteration.c +perf_namestore_api_zone_iteration_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_iteration_nick_sqlite_SOURCES = \ + test_namestore_api_zone_iteration_nick.c +test_namestore_api_zone_iteration_nick_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_iteration_nick_postgres_SOURCES = \ + test_namestore_api_zone_iteration_nick.c +test_namestore_api_zone_iteration_nick_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_iteration_specific_zone_sqlite_SOURCES = \ + test_namestore_api_zone_iteration_specific_zone.c +test_namestore_api_zone_iteration_specific_zone_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_iteration_specific_zone_postgres_SOURCES = \ + test_namestore_api_zone_iteration_specific_zone.c +test_namestore_api_zone_iteration_specific_zone_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_iteration_stop_sqlite_SOURCES = \ + test_namestore_api_zone_iteration_stop.c +test_namestore_api_zone_iteration_stop_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_namestore_api_zone_iteration_stop_postgres_SOURCES = \ + test_namestore_api_zone_iteration_stop.c +test_namestore_api_zone_iteration_stop_postgres_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la + +test_plugin_namestore_sqlite_SOURCES = \ + test_plugin_namestore.c +test_plugin_namestore_sqlite_LDADD = \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +test_plugin_namestore_postgres_SOURCES = \ + test_plugin_namestore.c +test_plugin_namestore_postgres_LDADD = \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/testing/libgnunettesting.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la + +check_SCRIPTS = \ + test_plugin_rest_namestore.sh + +EXTRA_DIST = \ + test_common.c \ + test_namestore_api.conf \ + test_namestore_api_postgres.conf \ + test_namestore_api_sqlite.conf \ + perf_namestore_api_postgres.conf \ + perf_namestore_api_sqlite.conf \ + test_plugin_namestore_sqlite.conf \ + test_plugin_namestore_postgres.conf \ + test_hostkey \ + $(check_SCRIPTS) \ + $(sql_DATA) diff --git a/src/plugin/namestore/namestore-0001.sql b/src/plugin/namestore/namestore-0001.sql new file mode 100644 index 000000000..bdfb31976 --- /dev/null +++ b/src/plugin/namestore/namestore-0001.sql @@ -0,0 +1,48 @@ +-- +-- This file is part of GNUnet +-- Copyright (C) 2014--2022 GNUnet e.V. +-- +-- GNUnet is free software; you can redistribute it and/or modify it under the +-- terms of the GNU General Public License as published by the Free Software +-- Foundation; either version 3, or (at your option) any later version. +-- +-- GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- GNUnet; see the file COPYING. If not, see +-- + +-- Everything in one big transaction +BEGIN; + +-- Check patch versioning is in place. +SELECT _v.register_patch('namestore-0001', NULL, NULL); + +-------------------- Schema ---------------------------- + +CREATE SCHEMA namestore; +COMMENT ON SCHEMA namestore IS 'gnunet-namestore data'; + +SET search_path TO namestore; + +CREATE TABLE ns098records ( + seq BIGSERIAL PRIMARY KEY, + zone_private_key BYTEA NOT NULL DEFAULT '', + pkey BYTEA DEFAULT '', + rvalue BYTEA NOT NULL DEFAULT '', + record_count INTEGER NOT NULL DEFAULT 0, + record_data BYTEA NOT NULL DEFAULT '', + label TEXT NOT NULL DEFAULT '', + CONSTRAINT zl UNIQUE (zone_private_key,label)); + +CREATE INDEX IF NOT EXISTS ir_pkey_reverse + ON ns098records (zone_private_key,pkey); +CREATE INDEX IF NOT EXISTS ir_pkey_iter + ON ns098records (zone_private_key,seq); +CREATE INDEX IF NOT EXISTS ir_label + ON ns098records (label); + + +COMMIT; diff --git a/src/plugin/namestore/namestore-drop.sql b/src/plugin/namestore/namestore-drop.sql new file mode 100644 index 000000000..231417af8 --- /dev/null +++ b/src/plugin/namestore/namestore-drop.sql @@ -0,0 +1,25 @@ +-- +-- This file is part of GNUnet +-- Copyright (C) 2014--2022 GNUnet e.V. +-- +-- GNUnet is free software; you can redistribute it and/or modify it under the +-- terms of the GNU General Public License as published by the Free Software +-- Foundation; either version 3, or (at your option) any later version. +-- +-- GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- GNUnet; see the file COPYING. If not, see +-- + +-- Everything in one big transaction +BEGIN; + + +SELECT _v.unregister_patch('namestore-0001'); + +DROP SCHEMA namestore CASCADE; + +COMMIT; diff --git a/src/plugin/namestore/perf_namestore_api_import.c b/src/plugin/namestore/perf_namestore_api_import.c new file mode 100644 index 000000000..e56fb961c --- /dev/null +++ b/src/plugin/namestore/perf_namestore_api_import.c @@ -0,0 +1,406 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/perf_namestore_api_import.c + * @brief testcase for namestore: Import a lot of records + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_COUNT 10000 + +/** + * A #BENCHMARK_SIZE of 1000 takes less than a minute on a reasonably + * modern system, so 30 minutes should be OK even for very, very + * slow systems. + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30) + +/** + * The runtime of the benchmark is expected to be linear + * for the iteration phase with a *good* database. The FLAT + * database uses a quadratic retrieval algorithm, + * hence it should be quadratic in the size. + */ +#define BENCHMARK_SIZE 1000 + +/** + * Maximum record size + */ +#define MAX_REC_SIZE 500 + +/** + * How big are the blocks we fetch? Note that the first block is + * always just 1 record set per current API. Smaller block + * sizes will make quadratic iteration-by-offset penalties + * more pronounced. + */ +#define BLOCK_SIZE 100 + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *timeout_task; + +static struct GNUNET_SCHEDULER_Task *t; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_NAMESTORE_QueueEntry *qe; + +static int res; + +static struct GNUNET_TIME_Absolute start; + +struct GNUNET_NAMESTORE_RecordInfo ri[TEST_RECORD_COUNT]; + +int single_put_pos; + +static int bulk_count = 0; + + +/** + * Terminate everything + * + * @param cls NULL + */ +static void +end (void *cls) +{ + (void) cls; + if (NULL != qe) + { + GNUNET_NAMESTORE_cancel (qe); + qe = NULL; + } + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + if (NULL != t) + { + GNUNET_SCHEDULER_cancel (t); + t = NULL; + } + if (NULL != timeout_task) + { + GNUNET_SCHEDULER_cancel (timeout_task); + timeout_task = NULL; + } +} + + +/** + * End with timeout. As this is a benchmark, we do not + * fail hard but return "skipped". + */ +static void +timeout (void *cls) +{ + (void) cls; + timeout_task = NULL; + GNUNET_SCHEDULER_shutdown (); + res = 77; +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_malloc (count + sizeof(struct GNUNET_GNSRECORD_Data)); + rd->expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd->record_type = TEST_RECORD_TYPE; + rd->data_size = count; + rd->data = (void *) &rd[1]; + rd->flags = 0; + memset (&rd[1], + 'a', + count); + return rd; +} + + +static void +publish_records_single (void *cls); + +static void +commit_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + struct GNUNET_TIME_Relative delay; + + (void) cls; + qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + single_put_pos++; + delay = GNUNET_TIME_absolute_get_duration (start); + fprintf (stdout, + "BULK-TX: Publishing %u records took %s\n", + TEST_RECORD_COUNT, + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES)); + res = 0; + GNUNET_SCHEDULER_shutdown (); +} + +static void +publish_records_bulk_tx (void *cls); + + +static void +put_cont_bulk_tx (void *cls, + enum GNUNET_ErrorCode ec) +{ + qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + if (bulk_count == TEST_RECORD_COUNT) + { + qe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont, NULL); + return; + } + t = GNUNET_SCHEDULER_add_now (&publish_records_bulk_tx, NULL); +} + + +static void +publish_records_bulk_tx (void *cls) +{ + unsigned int sent_rds; + t = NULL; + qe = GNUNET_NAMESTORE_records_store2 (nsh, + &privkey, + TEST_RECORD_COUNT - bulk_count, + &ri[bulk_count], + &sent_rds, + &put_cont_bulk_tx, + NULL); + bulk_count += sent_rds; + GNUNET_assert (sent_rds != 0); +} + + +static void +begin_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + unsigned int sent_rds; + qe = GNUNET_NAMESTORE_records_store2 (nsh, + &privkey, + TEST_RECORD_COUNT - bulk_count, + &ri[bulk_count], + &sent_rds, + &put_cont_bulk_tx, + NULL); + bulk_count += sent_rds; + GNUNET_assert (sent_rds != 0); +} + +static void +publish_records_bulk (void *cls); + +static void +put_cont_bulk (void *cls, + enum GNUNET_ErrorCode ec) +{ + struct GNUNET_TIME_Relative delay; + + (void) cls; + qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (bulk_count == TEST_RECORD_COUNT) + { + delay = GNUNET_TIME_absolute_get_duration (start); + fprintf (stdout, + "BULK: Publishing %u records took %s\n", + TEST_RECORD_COUNT, + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES)); + start = GNUNET_TIME_absolute_get (); + bulk_count = 0; + qe = GNUNET_NAMESTORE_transaction_begin (nsh, begin_cont, NULL); + return; + } + (void) cls; + qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + t = GNUNET_SCHEDULER_add_now (&publish_records_bulk, NULL); +} + +static void +publish_records_bulk (void *cls) +{ + static unsigned int sent_rds = 0; + (void) cls; + t = NULL; + qe = GNUNET_NAMESTORE_records_store2 (nsh, + &privkey, + TEST_RECORD_COUNT - bulk_count, + &ri[bulk_count], + &sent_rds, + &put_cont_bulk, + NULL); + bulk_count += sent_rds; + GNUNET_assert (sent_rds != 0); +} + + +static void +put_cont_single (void *cls, + enum GNUNET_ErrorCode ec) +{ + struct GNUNET_TIME_Relative delay; + (void) cls; + qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + single_put_pos++; + if (single_put_pos == TEST_RECORD_COUNT) + { + delay = GNUNET_TIME_absolute_get_duration (start); + fprintf (stdout, + "SINGLE: Publishing %u records took %s\n", + TEST_RECORD_COUNT, + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES)); + start = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_now (&publish_records_bulk, NULL); + return; + } + t = GNUNET_SCHEDULER_add_now (&publish_records_single, + NULL); +} + + +static void +publish_records_single (void *cls) +{ + struct GNUNET_TIME_Relative delay; + + (void) cls; + t = NULL; + if (single_put_pos == TEST_RECORD_COUNT) + { + delay = GNUNET_TIME_absolute_get_duration (start); + fprintf (stdout, + "Publishing %u records took %s\n", + TEST_RECORD_COUNT, + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES)); + GNUNET_SCHEDULER_add_now (&publish_records_bulk, NULL); + } + qe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + ri[single_put_pos].a_label, + ri[single_put_pos].a_rd_count, + ri[single_put_pos].a_rd, + &put_cont_single, + NULL); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + + for (int i = 0; i < TEST_RECORD_COUNT; i++) + { + ri[i].a_rd = create_record (1); + ri[i].a_rd_count = 1; + GNUNET_asprintf ((char**) &ri[i].a_label, "label_%d", i); + } + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &timeout, + NULL); + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_assert (NULL != nsh); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + start = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_now (&publish_records_single, + NULL); +} + + +#include "test_common.c" + + +int +main (int argc, + char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG2 ("perf_namestore_api_%s.conf", plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("perf-namestore-api-import", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of perf_namestore_api_zone_iteration.c */ diff --git a/src/plugin/namestore/perf_namestore_api_postgres.conf b/src/plugin/namestore/perf_namestore_api_postgres.conf new file mode 100644 index 000000000..5e02c2df3 --- /dev/null +++ b/src/plugin/namestore/perf_namestore_api_postgres.conf @@ -0,0 +1,12 @@ +@INLINE@ test_namestore_api_postgres.conf + +[namestore] +DATABASE = postgres + +[namecache] +DISABLE = YES + +[namestore-postgres] +CONFIG = connect_timeout=10 dbname=gnunetcheck +#TEMPORARY_TABLE = YES +INIT_ON_CONNECT = YES diff --git a/src/plugin/namestore/perf_namestore_api_sqlite.conf b/src/plugin/namestore/perf_namestore_api_sqlite.conf new file mode 100644 index 000000000..55c3dc812 --- /dev/null +++ b/src/plugin/namestore/perf_namestore_api_sqlite.conf @@ -0,0 +1,8 @@ +@INLINE@ test_namestore_api.conf + +[namecache] +DISABLE = YES + +[namestore-sqlite] +FILENAME = $GNUNET_TEST_HOME/namestore/sqlite_test.db +INIT_ON_CONNECT = YES diff --git a/src/plugin/namestore/perf_namestore_api_zone_iteration.c b/src/plugin/namestore/perf_namestore_api_zone_iteration.c new file mode 100644 index 000000000..e16748f5b --- /dev/null +++ b/src/plugin/namestore/perf_namestore_api_zone_iteration.c @@ -0,0 +1,378 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/perf_namestore_api_zone_iteration.c + * @brief testcase for zone iteration functionality: iterate all zones + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +/** + * A #BENCHMARK_SIZE of 1000 takes less than a minute on a reasonably + * modern system, so 30 minutes should be OK even for very, very + * slow systems. + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30) + +/** + * The runtime of the benchmark is expected to be linear + * for the iteration phase with a *good* database. The FLAT + * database uses a quadratic retrieval algorithm, + * hence it should be quadratic in the size. + */ +#define BENCHMARK_SIZE 1000 + +/** + * Maximum record size + */ +#define MAX_REC_SIZE 500 + +/** + * How big are the blocks we fetch? Note that the first block is + * always just 1 record set per current API. Smaller block + * sizes will make quadratic iteration-by-offset penalties + * more pronounced. + */ +#define BLOCK_SIZE 100 + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *timeout_task; + +static struct GNUNET_SCHEDULER_Task *t; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_NAMESTORE_ZoneIterator *zi; + +static struct GNUNET_NAMESTORE_QueueEntry *qe; + +static int res; + +static unsigned int off; + +static unsigned int left_until_next; + +static uint8_t seen[1 + BENCHMARK_SIZE / 8]; + +static struct GNUNET_TIME_Absolute start; + + +/** + * Terminate everything + * + * @param cls NULL + */ +static void +end (void *cls) +{ + (void) cls; + if (NULL != qe) + { + GNUNET_NAMESTORE_cancel (qe); + qe = NULL; + } + if (NULL != zi) + { + GNUNET_NAMESTORE_zone_iteration_stop (zi); + zi = NULL; + } + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + if (NULL != t) + { + GNUNET_SCHEDULER_cancel (t); + t = NULL; + } + if (NULL != timeout_task) + { + GNUNET_SCHEDULER_cancel (timeout_task); + timeout_task = NULL; + } +} + + +/** + * End with timeout. As this is a benchmark, we do not + * fail hard but return "skipped". + */ +static void +timeout (void *cls) +{ + (void) cls; + timeout_task = NULL; + GNUNET_SCHEDULER_shutdown (); + res = 77; +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_malloc (count + sizeof(struct GNUNET_GNSRECORD_Data)); + rd->expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd->record_type = TEST_RECORD_TYPE; + rd->data_size = count; + rd->data = (void *) &rd[1]; + rd->flags = 0; + memset (&rd[1], + 'a', + count); + return rd; +} + + +static void +zone_end (void *cls) +{ + struct GNUNET_TIME_Relative delay; + + zi = NULL; + delay = GNUNET_TIME_absolute_get_duration (start); + fprintf (stdout, + "Iterating over %u records took %s\n", + off, + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES)); + if (BENCHMARK_SIZE == off) + { + res = 0; + } + else + { + GNUNET_break (0); + res = 1; + } + GNUNET_SCHEDULER_shutdown (); +} + + +static void +fail_cb (void *cls) +{ + zi = NULL; + res = 2; + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct GNUNET_GNSRECORD_Data *wrd; + unsigned int xoff; + + GNUNET_assert (NULL != zone); + if (1 != sscanf (label, + "l%u", + &xoff)) + { + res = 3; + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + if ((xoff > BENCHMARK_SIZE) || + (0 != (seen[xoff / 8] & (1U << (xoff % 8))))) + { + res = 3; + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + seen[xoff / 8] |= (1U << (xoff % 8)); + wrd = create_record (xoff % MAX_REC_SIZE); + if ((rd->record_type != wrd->record_type) || + (rd->data_size != wrd->data_size) || + (rd->flags != wrd->flags)) + { + res = 4; + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + GNUNET_free (wrd); + return; + } + if (0 != memcmp (rd->data, + wrd->data, + wrd->data_size)) + { + res = 4; + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + GNUNET_free (wrd); + return; + } + GNUNET_free (wrd); + if (0 != GNUNET_memcmp (zone, + &privkey)) + { + res = 5; + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + off++; + left_until_next--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Obtained record %u, expecting %u more until asking for more explicitly\n", + off, + left_until_next); + if (0 == left_until_next) + { + left_until_next = BLOCK_SIZE; + GNUNET_NAMESTORE_zone_iterator_next (zi, + left_until_next); + } +} + + +static void +publish_record (void *cls); + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + (void) cls; + qe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + t = GNUNET_SCHEDULER_add_now (&publish_record, + NULL); +} + + +static void +publish_record (void *cls) +{ + struct GNUNET_GNSRECORD_Data *rd; + char *label; + + (void) cls; + t = NULL; + if (BENCHMARK_SIZE == off) + { + struct GNUNET_TIME_Relative delay; + + delay = GNUNET_TIME_absolute_get_duration (start); + fprintf (stdout, + "Inserting %u records took %s\n", + off, + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES)); + start = GNUNET_TIME_absolute_get (); + off = 0; + left_until_next = 1; + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &zone_proc, + NULL, + &zone_end, + NULL); + GNUNET_assert (NULL != zi); + return; + } + rd = create_record ((++off) % MAX_REC_SIZE); + GNUNET_asprintf (&label, + "l%u", + off); + qe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + label, + 1, rd, + &put_cont, + NULL); + GNUNET_free (label); + GNUNET_free (rd); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &timeout, + NULL); + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_assert (NULL != nsh); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + start = GNUNET_TIME_absolute_get (); + t = GNUNET_SCHEDULER_add_now (&publish_record, + NULL); +} + + +#include "test_common.c" + + +int +main (int argc, + char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("perf-namestore-api-zone-iteration", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of perf_namestore_api_zone_iteration.c */ diff --git a/src/plugin/namestore/plugin_namestore_flat.c b/src/plugin/namestore/plugin_namestore_flat.c new file mode 100644 index 000000000..716071ff3 --- /dev/null +++ b/src/plugin/namestore/plugin_namestore_flat.c @@ -0,0 +1,817 @@ +/* + * This file is part of GNUnet + * Copyright (C) 2009-2015, 2018, 2019 GNUnet e.V. + * + * GNUnet is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * GNUnet is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/plugin_namestore_flat.c + * @brief file-based namestore backend + * @author Martin Schanzenbach + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_namestore_plugin.h" +#include "gnunet_namestore_service.h" +#include "gnunet_gnsrecord_lib.h" + +/** + * Context for all functions in this plugin. + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Database filename. + */ + char *fn; + + /** + * HashMap + */ + struct GNUNET_CONTAINER_MultiHashMap *hm; +}; + + +struct FlatFileEntry +{ + /** + * Entry zone + */ + struct GNUNET_CRYPTO_PrivateKey private_key; + + /** + * Record count. + */ + uint32_t record_count; + + /** + * Rvalue + */ + uint64_t rvalue; + + /** + * Record data + */ + struct GNUNET_GNSRECORD_Data *record_data; + + /** + * Label + */ + char *label; +}; + + +/** + * Hash contactenation of @a pkey and @a label into @a h + * + * @param pkey a key + * @param label a label + * @param[out] h initialized hash + */ +static void +hash_pkey_and_label (const struct GNUNET_CRYPTO_PrivateKey *pkey, + const char *label, + struct GNUNET_HashCode *h) +{ + char *key; + size_t label_len; + size_t key_len; + + label_len = strlen (label); + key_len = label_len + sizeof(struct GNUNET_CRYPTO_PrivateKey); + key = GNUNET_malloc (key_len); + GNUNET_memcpy (key, + label, + label_len); + GNUNET_memcpy (key + label_len, + pkey, + sizeof(struct GNUNET_CRYPTO_PrivateKey)); + GNUNET_CRYPTO_hash (key, + key_len, + h); + GNUNET_free (key); +} + + +/** + * Initialize the database connections and associated + * data structures (create tables and indices + * as needed as well). + * + * @param plugin the plugin context (state for this module) + * @return #GNUNET_OK on success + */ +static int +database_setup (struct Plugin *plugin) +{ + char *flatdbfile; + char *record_data; + char *zone_private_key; + char *record_data_b64; + char *buffer; + char *line; + char *label; + char *rvalue; + char *record_count; + size_t record_data_size; + uint64_t size; + struct GNUNET_HashCode hkey; + struct GNUNET_DISK_FileHandle *fh; + struct FlatFileEntry *entry; + struct GNUNET_DISK_MapHandle *mh; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, + "namestore-flat", + "FILENAME", + &flatdbfile)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "namestore-flat", + "FILENAME"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_DISK_file_test (flatdbfile)) + { + if (GNUNET_OK != + GNUNET_DISK_directory_create_for_file (flatdbfile)) + { + GNUNET_break (0); + GNUNET_free (flatdbfile); + return GNUNET_SYSERR; + } + } + /* flatdbfile should be UTF-8-encoded. If it isn't, it's a bug */ + plugin->fn = flatdbfile; + + /* Load data from file into hashmap */ + plugin->hm = GNUNET_CONTAINER_multihashmap_create (10, + GNUNET_NO); + fh = GNUNET_DISK_file_open (flatdbfile, + GNUNET_DISK_OPEN_CREATE + | GNUNET_DISK_OPEN_READWRITE, + GNUNET_DISK_PERM_USER_WRITE + | GNUNET_DISK_PERM_USER_READ); + if (NULL == fh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to initialize file: %s.\n"), + flatdbfile); + return GNUNET_SYSERR; + } + if (GNUNET_SYSERR == + GNUNET_DISK_file_size (flatdbfile, + &size, + GNUNET_YES, + GNUNET_YES)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to get filesize: %s.\n"), + flatdbfile); + GNUNET_DISK_file_close (fh); + return GNUNET_SYSERR; + } + if (size > SIZE_MAX) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("File too big to map: %llu bytes.\n"), + (unsigned long long) size); + GNUNET_DISK_file_close (fh); + return GNUNET_SYSERR; + } + if (0 == size) + { + GNUNET_DISK_file_close (fh); + return GNUNET_OK; + } + buffer = GNUNET_DISK_file_map (fh, + &mh, + GNUNET_DISK_MAP_TYPE_READ, + size); + if (NULL == buffer) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "mmap"); + GNUNET_DISK_file_close (fh); + return GNUNET_SYSERR; + } + if ('\0' != buffer[size - 1]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore database file `%s' malformed\n"), + flatdbfile); + GNUNET_DISK_file_unmap (mh); + GNUNET_DISK_file_close (fh); + return GNUNET_SYSERR; + } + + line = strtok (buffer, "\n"); + while (NULL != line) + { + zone_private_key = strtok (line, ","); + if (NULL == zone_private_key) + break; + rvalue = strtok (NULL, ","); + if (NULL == rvalue) + break; + record_count = strtok (NULL, ","); + if (NULL == record_count) + break; + record_data_b64 = strtok (NULL, ","); + if (NULL == record_data_b64) + break; + label = strtok (NULL, ","); + if (NULL == label) + break; + line = strtok (NULL, "\n"); + entry = GNUNET_new (struct FlatFileEntry); + { + unsigned long long ll; + + if (1 != sscanf (rvalue, + "%llu", + &ll)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error parsing entry\n"); + GNUNET_free (entry); + break; + } + entry->rvalue = (uint64_t) ll; + } + { + unsigned int ui; + + if (1 != sscanf (record_count, + "%u", + &ui)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error parsing entry\n"); + GNUNET_free (entry); + break; + } + entry->record_count = (uint32_t) ui; + } + entry->label = GNUNET_strdup (label); + record_data_size + = GNUNET_STRINGS_base64_decode (record_data_b64, + strlen (record_data_b64), + (void **) &record_data); + entry->record_data = + GNUNET_new_array (entry->record_count, + struct GNUNET_GNSRECORD_Data); + if (GNUNET_OK != + GNUNET_GNSRECORD_records_deserialize (record_data_size, + record_data, + entry->record_count, + entry->record_data)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to deserialize record %s\n", + label); + GNUNET_free (entry->label); + GNUNET_free (entry); + GNUNET_free (record_data); + break; + } + GNUNET_free (record_data); + + { + struct GNUNET_CRYPTO_PrivateKey *private_key; + + GNUNET_STRINGS_base64_decode (zone_private_key, + strlen (zone_private_key), + (void **) &private_key); + entry->private_key = *private_key; + GNUNET_free (private_key); + } + + hash_pkey_and_label (&entry->private_key, + label, + &hkey); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (plugin->hm, + &hkey, + entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_free (entry); + GNUNET_break (0); + } + } + GNUNET_DISK_file_unmap (mh); + GNUNET_DISK_file_close (fh); + return GNUNET_OK; +} + + +/** + * Store values in hashmap in file and free data + * + * @param plugin the plugin context + * @param key key in the map + * @param value a `struct FlatFileEntry` + */ +static int +store_and_free_entries (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_DISK_FileHandle *fh = cls; + struct FlatFileEntry *entry = value; + char *line; + char *zone_private_key; + char *record_data_b64; + ssize_t data_size; + + (void) key; + GNUNET_STRINGS_base64_encode (&entry->private_key, + sizeof(struct GNUNET_CRYPTO_PrivateKey), + &zone_private_key); + data_size = GNUNET_GNSRECORD_records_get_size (entry->record_count, + entry->record_data); + if (data_size < 0) + { + GNUNET_break (0); + GNUNET_free (zone_private_key); + return GNUNET_SYSERR; + } + if (data_size >= UINT16_MAX) + { + GNUNET_break (0); + GNUNET_free (zone_private_key); + return GNUNET_SYSERR; + } + { + char data[data_size]; + ssize_t ret; + + ret = GNUNET_GNSRECORD_records_serialize (entry->record_count, + entry->record_data, + data_size, + data); + if ((ret < 0) || + (data_size != ret)) + { + GNUNET_break (0); + GNUNET_free (zone_private_key); + return GNUNET_SYSERR; + } + GNUNET_STRINGS_base64_encode (data, + data_size, + &record_data_b64); + } + GNUNET_asprintf (&line, + "%s,%llu,%u,%s,%s\n", + zone_private_key, + (unsigned long long) entry->rvalue, + (unsigned int) entry->record_count, + record_data_b64, + entry->label); + GNUNET_free (record_data_b64); + GNUNET_free (zone_private_key); + + GNUNET_DISK_file_write (fh, + line, + strlen (line)); + + GNUNET_free (line); + GNUNET_free (entry->label); + GNUNET_free (entry->record_data); + GNUNET_free (entry); + return GNUNET_YES; +} + + +/** + * Shutdown database connection and associate data + * structures. + * @param plugin the plugin context (state for this module) + */ +static void +database_shutdown (struct Plugin *plugin) +{ + struct GNUNET_DISK_FileHandle *fh; + + fh = GNUNET_DISK_file_open (plugin->fn, + GNUNET_DISK_OPEN_CREATE + | GNUNET_DISK_OPEN_TRUNCATE + | GNUNET_DISK_OPEN_READWRITE, + GNUNET_DISK_PERM_USER_WRITE + | GNUNET_DISK_PERM_USER_READ); + if (NULL == fh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to initialize file: %s.\n"), + plugin->fn); + return; + } + + GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, + &store_and_free_entries, + fh); + GNUNET_CONTAINER_multihashmap_destroy (plugin->hm); + /* append 0-terminator */ + GNUNET_DISK_file_write (fh, + "", + 1); + GNUNET_DISK_file_close (fh); +} + + +/** + * Store a record in the datastore. Removes any existing record in the + * same zone with the same name. + * + * @param cls closure (internal context for the plugin) + * @param zone_key private key of the zone + * @param label name that is being mapped (at most 255 characters long) + * @param rd_count number of entries in @a rd array + * @param rd array of records with data to store + * @return #GNUNET_OK on success, else #GNUNET_SYSERR + */ +static int +namestore_flat_store_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone_key, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct Plugin *plugin = cls; + uint64_t rvalue; + struct GNUNET_HashCode hkey; + struct FlatFileEntry *entry; + + rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); + hash_pkey_and_label (zone_key, + label, + &hkey); + GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm, + &hkey); + if (0 == rd_count) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", + "Record deleted\n"); + return GNUNET_OK; + } + entry = GNUNET_new (struct FlatFileEntry); + GNUNET_asprintf (&entry->label, + label, + strlen (label)); + GNUNET_memcpy (&entry->private_key, + zone_key, + sizeof(struct GNUNET_CRYPTO_PrivateKey)); + entry->rvalue = rvalue; + entry->record_count = rd_count; + entry->record_data = GNUNET_new_array (rd_count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int i = 0; i < rd_count; i++) + { + entry->record_data[i].expiration_time = rd[i].expiration_time; + entry->record_data[i].record_type = rd[i].record_type; + entry->record_data[i].flags = rd[i].flags; + entry->record_data[i].data_size = rd[i].data_size; + entry->record_data[i].data = GNUNET_malloc (rd[i].data_size); + GNUNET_memcpy ((char *) entry->record_data[i].data, + rd[i].data, + rd[i].data_size); + } + return GNUNET_CONTAINER_multihashmap_put (plugin->hm, + &hkey, + entry, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); +} + + +/** + * Lookup records in the datastore for which we are the authority. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone + * @param label name of the record in the zone + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR + */ +static int +namestore_flat_lookup_records (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + struct FlatFileEntry *entry; + struct GNUNET_HashCode hkey; + + if (NULL == zone) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + hash_pkey_and_label (zone, + label, + &hkey); + entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm, + &hkey); + + if (NULL == entry) + return GNUNET_NO; + if (NULL != iter) + iter (iter_cls, + 1, /* zero is illegal */ + &entry->private_key, + entry->label, + entry->record_count, + entry->record_data); + return GNUNET_YES; +} + + +/** + * Closure for #iterate_zones. + */ +struct IterateContext +{ + /** + * How many more records should we skip before returning results? + */ + uint64_t offset; + + /** + * How many more records should we return? + */ + uint64_t limit; + + /** + * What is the position of the current entry, counting + * starts from 1. + */ + uint64_t pos; + + /** + * Target zone. + */ + const struct GNUNET_CRYPTO_PrivateKey *zone; + + /** + * Function to call on each record. + */ + GNUNET_NAMESTORE_RecordIterator iter; + + /** + * Closure for @e iter. + */ + void *iter_cls; +}; + + +/** + * Helper function for #namestore_flat_iterate_records(). + * + * @param cls a `struct IterateContext` + * @param key unused + * @param value a `struct FlatFileEntry` + * @return #GNUNET_YES to continue the iteration + */ +static int +iterate_zones (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct IterateContext *ic = cls; + struct FlatFileEntry *entry = value; + + (void) key; + if (0 == ic->limit) + return GNUNET_NO; + if ((NULL != ic->zone) && + (0 != GNUNET_memcmp (&entry->private_key, + ic->zone))) + return GNUNET_YES; + ic->pos++; + if (ic->offset > 0) + { + ic->offset--; + return GNUNET_YES; + } + ic->iter (ic->iter_cls, + ic->pos, + (NULL == ic->zone) + ? &entry->private_key + : ic->zone, + entry->label, + entry->record_count, + entry->record_data); + ic->limit--; + if (0 == ic->limit) + return GNUNET_NO; + return GNUNET_YES; +} + + +/** + * Iterate over the results for a particular key and zone in the + * datastore. Will return at most one result to the iterator. + * + * @param cls closure (internal context for the plugin) + * @param zone hash of public key of the zone, NULL to iterate over all zones + * @param serial serial number to exclude in the list of all matching records + * @param limit maximum number of results to return to @a iter + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error + */ +static int +namestore_flat_iterate_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + uint64_t serial, + uint64_t limit, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + struct IterateContext ic; + + ic.offset = serial; + ic.pos = 0; + ic.limit = limit; + ic.iter = iter; + ic.iter_cls = iter_cls; + ic.zone = zone; + GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, + &iterate_zones, + &ic); + return (0 == ic.limit) ? GNUNET_OK : GNUNET_NO; +} + + +/** + * Closure for #zone_to_name. + */ +struct ZoneToNameContext +{ + const struct GNUNET_CRYPTO_PrivateKey *zone; + const struct GNUNET_CRYPTO_PublicKey *value_zone; + GNUNET_NAMESTORE_RecordIterator iter; + void *iter_cls; + + int result_found; +}; + + +static int +zone_to_name (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct ZoneToNameContext *ztn = cls; + struct FlatFileEntry *entry = value; + + (void) key; + if (0 != GNUNET_memcmp (&entry->private_key, + ztn->zone)) + return GNUNET_YES; + + for (unsigned int i = 0; i < entry->record_count; i++) + { + if (GNUNET_NO == + GNUNET_GNSRECORD_is_zonekey_type (entry->record_data[i].record_type)) + continue; + if (ztn->value_zone->type != entry->record_data[i].record_type) + continue; + if (0 == memcmp (ztn->value_zone, + entry->record_data[i].data, + entry->record_data[i].data_size)) + { + ztn->iter (ztn->iter_cls, + i + 1, /* zero is illegal! */ + &entry->private_key, + entry->label, + entry->record_count, + entry->record_data); + ztn->result_found = GNUNET_YES; + } + } + return GNUNET_YES; +} + + +/** + * Look for an existing PKEY delegation record for a given public key. + * Returns at most one result to the iterator. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone to look up in, never NULL + * @param value_zone public key of the target zone (value), never NULL + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error + */ +static int +namestore_flat_zone_to_name (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const struct + GNUNET_CRYPTO_PublicKey *value_zone, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + struct ZoneToNameContext ztn = { + .iter = iter, + .iter_cls = iter_cls, + .zone = zone, + .value_zone = value_zone, + .result_found = GNUNET_NO + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Performing reverse lookup for `%s'\n", + GNUNET_GNSRECORD_z2s (value_zone)); + GNUNET_CONTAINER_multihashmap_iterate (plugin->hm, + &zone_to_name, + &ztn); + return ztn.result_found; +} + + +/** + * Entry point for the plugin. + * + * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*" + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_namestore_flat_init (void *cls) +{ + static struct Plugin plugin; + const struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct GNUNET_NAMESTORE_PluginFunctions *api; + + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, + 0, + sizeof(struct Plugin)); + plugin.cfg = cfg; + if (GNUNET_OK != database_setup (&plugin)) + { + database_shutdown (&plugin); + return NULL; + } + api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions); + api->cls = &plugin; + api->store_records = &namestore_flat_store_records; + api->iterate_records = &namestore_flat_iterate_records; + api->zone_to_name = &namestore_flat_zone_to_name; + api->lookup_records = &namestore_flat_lookup_records; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Flat file database running\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_namestore_flat_done (void *cls) +{ + struct GNUNET_NAMESTORE_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + database_shutdown (plugin); + plugin->cfg = NULL; + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Flat file plugin is finished\n"); + return NULL; +} + + +/* end of plugin_namestore_flat.c */ diff --git a/src/plugin/namestore/plugin_namestore_postgres.c b/src/plugin/namestore/plugin_namestore_postgres.c new file mode 100644 index 000000000..1dc526dc2 --- /dev/null +++ b/src/plugin/namestore/plugin_namestore_postgres.c @@ -0,0 +1,795 @@ +/* + * This file is part of GNUnet + * Copyright (C) 2009-2013, 2016-2018 GNUnet e.V. + * + * GNUnet is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * GNUnet is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file namestore/plugin_namestore_postgres.c + * @brief postgres-based namestore backend + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_namestore_plugin.h" +#include "gnunet_namestore_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_pq_lib.h" + + +#define LOG(kind, ...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__) + + +/** + * Context for all functions in this plugin. + */ +struct Plugin +{ + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Postgres database handle. + */ + struct GNUNET_PQ_Context *dbh; + + /** + * Database is prepared and ready + */ + bool ready; +}; + + +/** + * Initialize the database connections and associated data structures (create + * tables and indices as needed as well). + * + * @param cls the plugin context (state for this module) + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_create_tables (void *cls) +{ + struct Plugin *plugin = cls; + struct GNUNET_PQ_Context *dbh; + + dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, + "namestore-postgres", + "namestore-", + NULL, + NULL); + if (NULL == dbh) + return GNUNET_SYSERR; + GNUNET_PQ_disconnect (dbh); + return GNUNET_OK; +} + + +/** + * Drop existing namestore tables. + * + * @param cls the plugin context (state for this module) + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_drop_tables (void *cls) +{ + struct Plugin *plugin = cls; + struct GNUNET_PQ_Context *dbh; + enum GNUNET_GenericReturnValue ret; + + dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, + "namestore-postgres", + NULL, + NULL, + NULL); + if (NULL == dbh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to database\n"); + return GNUNET_SYSERR; + } + ret = GNUNET_PQ_exec_sql (dbh, + "namestore-drop"); + GNUNET_PQ_disconnect (dbh); + return ret; +} + + +static enum GNUNET_GenericReturnValue +database_prepare (struct Plugin *plugin) +{ + enum GNUNET_GenericReturnValue ret; + + if (plugin->ready) + return GNUNET_OK; + { + struct GNUNET_PQ_PreparedStatement ps[] = { + GNUNET_PQ_make_prepare ("store_records", + "INSERT INTO namestore.ns098records" + " (zone_private_key, pkey, rvalue, record_count, record_data, label)" + " VALUES ($1, $2, $3, $4, $5, $6)" + " ON CONFLICT ON CONSTRAINT zl" + " DO UPDATE" + " SET pkey=$2,rvalue=$3,record_count=$4,record_data=$5" + " WHERE ns098records.zone_private_key = $1" + " AND ns098records.label = $6"), + GNUNET_PQ_make_prepare ("delete_records", + "DELETE FROM namestore.ns098records " + "WHERE zone_private_key=$1 AND label=$2"), + GNUNET_PQ_make_prepare ("zone_to_name", + "SELECT seq,record_count,record_data,label FROM namestore.ns098records" + " WHERE zone_private_key=$1 AND pkey=$2"), + GNUNET_PQ_make_prepare ("iterate_zone", + "SELECT seq,record_count,record_data,label FROM namestore.ns098records " + "WHERE zone_private_key=$1 AND seq > $2 ORDER BY seq ASC LIMIT $3"), + GNUNET_PQ_make_prepare ("iterate_all_zones", + "SELECT seq,record_count,record_data,label,zone_private_key" + " FROM namestore.ns098records WHERE seq > $1 ORDER BY seq ASC LIMIT $2"), + GNUNET_PQ_make_prepare ("lookup_label", + "SELECT seq,record_count,record_data,label " + "FROM namestore.ns098records WHERE zone_private_key=$1 AND label=$2"), + GNUNET_PQ_make_prepare ("edit_set", + "SELECT seq,record_count,record_data,label " + "FROM namestore.ns098records WHERE zone_private_key=$1 AND label=$2 FOR UPDATE NOWAIT"), + GNUNET_PQ_PREPARED_STATEMENT_END + }; + + ret = GNUNET_PQ_prepare_statements (plugin->dbh, + ps); + } + if (GNUNET_OK != ret) + return ret; + plugin->ready = true; + return GNUNET_OK; +} + + +/** + * Initialize the database connections and associated + * data structures (create tables and indices + * as needed as well). + * + * @param plugin the plugin context (state for this module) + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +database_connect (struct Plugin *plugin) +{ + struct GNUNET_PQ_ExecuteStatement ess[] = { + GNUNET_PQ_make_try_execute ("SET synchronous_commit TO off"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + struct GNUNET_PQ_ExecuteStatement *es; + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, + "namestore-postgres", + "ASYNC_COMMIT")) + es = &ess[0]; + else + es = &ess[1]; + + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, + "namestore-postgres", + "INIT_ON_CONNECT")) + { + if (GNUNET_OK != + namestore_postgres_create_tables (plugin)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create tables\n"); + return GNUNET_SYSERR; + } + } + plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, + "namestore-postgres", + NULL, + es, + NULL); + if (NULL == plugin->dbh) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Store a record in the datastore. Removes any existing record in the + * same zone with the same name. + * + * @param cls closure (internal context for the plugin) + * @param zone_key private key of the zone + * @param label name that is being mapped (at most 255 characters long) + * @param rd_count number of entries in @a rd array + * @param rd array of records with data to store + * @return #GNUNET_OK on success, else #GNUNET_SYSERR + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_store_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone_key, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct Plugin *plugin = cls; + struct GNUNET_CRYPTO_PublicKey pkey; + uint64_t rvalue; + uint32_t rd_count32 = (uint32_t) rd_count; + ssize_t data_size; + + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + memset (&pkey, + 0, + sizeof(pkey)); + for (unsigned int i = 0; i < rd_count; i++) + if (GNUNET_YES == + GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type)) + { + GNUNET_break (GNUNET_OK == + GNUNET_GNSRECORD_identity_from_data (rd[i].data, + rd[i].data_size, + rd[i].record_type, + &pkey)); + break; + } + rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); + data_size = GNUNET_GNSRECORD_records_get_size (rd_count, + rd); + if (data_size < 0) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (data_size >= UINT16_MAX) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + /* if record set is empty, delete existing records */ + if (0 == rd_count) + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (zone_key), + GNUNET_PQ_query_param_string (label), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus res; + + res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh, + "delete_records", + params); + if ((GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != res) && + (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != res)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "postgres", + "Record deleted\n"); + return GNUNET_OK; + } + /* otherwise, UPSERT (i.e. UPDATE if exists, otherwise INSERT) */ + { + char data[data_size]; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (zone_key), + GNUNET_PQ_query_param_auto_from_type (&pkey), + GNUNET_PQ_query_param_uint64 (&rvalue), + GNUNET_PQ_query_param_uint32 (&rd_count32), + GNUNET_PQ_query_param_fixed_size (data, data_size), + GNUNET_PQ_query_param_string (label), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus res; + ssize_t ret; + + ret = GNUNET_GNSRECORD_records_serialize (rd_count, + rd, + data_size, + data); + if ((ret < 0) || + (data_size != ret)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh, + "store_records", + params); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != res) + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Closure for #parse_result_call_iterator. + */ +struct ParserContext +{ + /** + * Function to call for each result. + */ + GNUNET_NAMESTORE_RecordIterator iter; + + /** + * Closure for @e iter. + */ + void *iter_cls; + + /** + * Zone key, NULL if part of record. + */ + const struct GNUNET_CRYPTO_PrivateKey *zone_key; + + /** + * Number of results still to return (counted down by + * number of results given to iterator). + */ + uint64_t limit; +}; + + +/** + * A statement has been run. We should evaluate the result, and if possible + * call the @a iter in @a cls with the result. + * + * @param cls closure of type `struct ParserContext *` + * @param res the postgres result + * @param num_results the number of results in @a result + */ +static void +parse_result_call_iterator (void *cls, + PGresult *res, + unsigned int num_results) +{ + struct ParserContext *pc = cls; + + if (NULL == pc->iter) + return; /* no need to do more work */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got %d results from PQ.\n", num_results); + for (unsigned int i = 0; i < num_results; i++) + { + uint64_t serial; + void *data; + size_t data_size; + uint32_t record_count; + char *label; + struct GNUNET_CRYPTO_PrivateKey zk; + struct GNUNET_PQ_ResultSpec rs_with_zone[] = { + GNUNET_PQ_result_spec_uint64 ("seq", &serial), + GNUNET_PQ_result_spec_uint32 ("record_count", &record_count), + GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size), + GNUNET_PQ_result_spec_string ("label", &label), + GNUNET_PQ_result_spec_auto_from_type ("zone_private_key", &zk), + GNUNET_PQ_result_spec_end + }; + struct GNUNET_PQ_ResultSpec rs_without_zone[] = { + GNUNET_PQ_result_spec_uint64 ("seq", &serial), + GNUNET_PQ_result_spec_uint32 ("record_count", &record_count), + GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size), + GNUNET_PQ_result_spec_string ("label", &label), + GNUNET_PQ_result_spec_end + }; + struct GNUNET_PQ_ResultSpec *rs; + + rs = (NULL == pc->zone_key) ? rs_with_zone : rs_without_zone; + if (GNUNET_YES != + GNUNET_PQ_extract_result (res, + rs, + i)) + { + GNUNET_break (0); + return; + } + + if (record_count > 64 * 1024) + { + /* sanity check, don't stack allocate far too much just + because database might contain a large value here */ + GNUNET_break (0); + GNUNET_PQ_cleanup_result (rs); + return; + } + + { + struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (record_count)]; + + GNUNET_assert (0 != serial); + if (GNUNET_OK != + GNUNET_GNSRECORD_records_deserialize (data_size, + data, + record_count, + rd)) + { + GNUNET_break (0); + GNUNET_PQ_cleanup_result (rs); + return; + } + pc->iter (pc->iter_cls, + serial, + (NULL == pc->zone_key) ? &zk : pc->zone_key, + label, + record_count, + rd); + } + GNUNET_PQ_cleanup_result (rs); + } + pc->limit -= num_results; +} + + +/** + * Lookup records in the datastore for which we are the authority. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone + * @param label name of the record in the zone + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @param method the method to use "lookup_record" or "edit_set" + * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR + */ +static enum GNUNET_GenericReturnValue +lookup_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls, + const char*method) +{ + struct Plugin *plugin = cls; + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (zone), + GNUNET_PQ_query_param_string (label), + GNUNET_PQ_query_param_end + }; + struct ParserContext pc; + enum GNUNET_DB_QueryStatus res; + + if (NULL == zone) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + pc.iter = iter; + pc.iter_cls = iter_cls; + pc.zone_key = zone; + res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, + method, + params, + &parse_result_call_iterator, + &pc); + if (res < 0) + return GNUNET_SYSERR; + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res) + return GNUNET_NO; + return GNUNET_OK; +} + + +/** + * Lookup records in the datastore for which we are the authority. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone + * @param label name of the record in the zone + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_lookup_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + return lookup_records (cls, zone, label, iter, iter_cls, "lookup_label"); +} + + +/** + * Edit records in the datastore for which we are the authority. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone + * @param label name of the record in the zone + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR + */ +static int +namestore_postgres_edit_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + return lookup_records (cls, zone, label, iter, iter_cls, "edit_set"); +} + + +/** + * Iterate over the results for a particular key and zone in the + * datastore. Will return at most one result to the iterator. + * + * @param cls closure (internal context for the plugin) + * @param zone hash of public key of the zone, NULL to iterate over all zones + * @param serial serial number to exclude in the list of all matching records + * @param limit maximum number of results to fetch + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_iterate_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + uint64_t serial, + uint64_t limit, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + enum GNUNET_DB_QueryStatus res; + struct ParserContext pc; + + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + pc.iter = iter; + pc.iter_cls = iter_cls; + pc.zone_key = zone; + pc.limit = limit; + if (NULL == zone) + { + struct GNUNET_PQ_QueryParam params_without_zone[] = { + GNUNET_PQ_query_param_uint64 (&serial), + GNUNET_PQ_query_param_uint64 (&limit), + GNUNET_PQ_query_param_end + }; + + res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, + "iterate_all_zones", + params_without_zone, + &parse_result_call_iterator, + &pc); + } + else + { + struct GNUNET_PQ_QueryParam params_with_zone[] = { + GNUNET_PQ_query_param_auto_from_type (zone), + GNUNET_PQ_query_param_uint64 (&serial), + GNUNET_PQ_query_param_uint64 (&limit), + GNUNET_PQ_query_param_end + }; + + res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, + "iterate_zone", + params_with_zone, + &parse_result_call_iterator, + &pc); + } + if (res < 0) + return GNUNET_SYSERR; + + if ((GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res) || + (pc.limit > 0)) + return GNUNET_NO; + return GNUNET_OK; +} + + +/** + * Look for an existing PKEY delegation record for a given public key. + * Returns at most one result to the iterator. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone to look up in, never NULL + * @param value_zone public key of the target zone (value), never NULL + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_zone_to_name (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + const struct + GNUNET_CRYPTO_PublicKey *value_zone, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (zone), + GNUNET_PQ_query_param_auto_from_type (value_zone), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus res; + struct ParserContext pc; + + pc.iter = iter; + pc.iter_cls = iter_cls; + pc.zone_key = zone; + res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh, + "zone_to_name", + params, + &parse_result_call_iterator, + &pc); + if (res < 0) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Begin a transaction for a client. + * + * @param cls closure (internal context for the plugin) + * @param emsg error message set of return code is #GNUNET_SYSERR + * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_transaction_begin (void *cls, + char **emsg) +{ + struct Plugin *plugin = cls; + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("BEGIN"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + return GNUNET_PQ_exec_statements (plugin->dbh, es); +} + + +/** + * Commit a transaction for a client. + * This releases the lock on the database. + * + * @param cls closure (internal context for the plugin) + * @param emsg error message set of return code is #GNUNET_SYSERR + * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_transaction_rollback (void *cls, + char **emsg) +{ + struct Plugin *plugin = cls; + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("ROLLBACK"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + return GNUNET_PQ_exec_statements (plugin->dbh, es); +} + + +/** + * Roll back a transaction for a client. + * This releases the lock on the database. + * + * @param cls closure (internal context for the plugin) + * @param emsg error message set of return code is #GNUNET_SYSERR + * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. + */ +static enum GNUNET_GenericReturnValue +namestore_postgres_transaction_commit (void *cls, + char **emsg) +{ + struct Plugin *plugin = cls; + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("COMMIT"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + return GNUNET_PQ_exec_statements (plugin->dbh, es); +} + + +/** + * Shutdown database connection and associate data + * structures. + * + * @param plugin the plugin context (state for this module) + */ +static void +database_shutdown (struct Plugin *plugin) +{ + GNUNET_PQ_disconnect (plugin->dbh); + plugin->dbh = NULL; +} + + +/** + * Entry point for the plugin. + * + * @param cls the `struct GNUNET_NAMESTORE_PluginEnvironment*` + * @return NULL on error, othrewise the plugin context + */ +void * +libgnunet_plugin_namestore_postgres_init (void *cls) +{ + struct Plugin *plugin; + const struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct GNUNET_NAMESTORE_PluginFunctions *api; + + plugin = GNUNET_new (struct Plugin); + plugin->cfg = cfg; + if (GNUNET_OK != database_connect (plugin)) + { + database_shutdown (plugin); + GNUNET_free (plugin); + return NULL; + } + api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions); + api->cls = plugin; + api->create_tables = &namestore_postgres_create_tables; + api->drop_tables = &namestore_postgres_drop_tables; + api->store_records = &namestore_postgres_store_records; + api->iterate_records = &namestore_postgres_iterate_records; + api->zone_to_name = &namestore_postgres_zone_to_name; + api->lookup_records = &namestore_postgres_lookup_records; + api->transaction_begin = &namestore_postgres_transaction_begin; + api->transaction_commit = &namestore_postgres_transaction_commit; + api->transaction_rollback = &namestore_postgres_transaction_rollback; + api->edit_records = &namestore_postgres_edit_records; + LOG (GNUNET_ERROR_TYPE_INFO, + "Postgres namestore plugin running\n"); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_namestore_postgres_done (void *cls) +{ + struct GNUNET_NAMESTORE_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + database_shutdown (plugin); + plugin->cfg = NULL; + GNUNET_free (plugin); + GNUNET_free (api); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Postgres namestore plugin is finished\n"); + return NULL; +} + + +/* end of plugin_namestore_postgres.c */ diff --git a/src/plugin/namestore/plugin_namestore_sqlite.c b/src/plugin/namestore/plugin_namestore_sqlite.c new file mode 100644 index 000000000..ada9ca25b --- /dev/null +++ b/src/plugin/namestore/plugin_namestore_sqlite.c @@ -0,0 +1,982 @@ +/* + * This file is part of GNUnet + * Copyright (C) 2009-2017 GNUnet e.V. + * + * GNUnet is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * GNUnet is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file namestore/plugin_namestore_sqlite.c + * @brief sqlite-based namestore backend + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_namestore_plugin.h" +#include "gnunet_namestore_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_sq_lib.h" +#include + +/** + * After how many ms "busy" should a DB operation fail for good? A + * low value makes sure that we are more responsive to requests + * (especially PUTs). A high value guarantees a higher success rate + * (SELECTs in iterate can take several seconds despite LIMIT=1). + * + * The default value of 1s should ensure that users do not experience + * huge latencies while at the same time allowing operations to + * succeed with reasonable probability. + */ +#define BUSY_TIMEOUT_MS 1000 + + +/** + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' on file 'filename' + * with the message given by strerror(errno). + */ +#define LOG_SQLITE(db, level, cmd) do { \ + GNUNET_log_from (level, \ + "namestore-sqlite", _ ( \ + "`%s' failed at %s:%d with error: %s\n"), \ + cmd, \ + __FILE__, __LINE__, \ + sqlite3_errmsg ( \ + db->dbh)); \ +} while (0) + +#define LOG(kind, ...) GNUNET_log_from (kind, "namestore-sqlite", __VA_ARGS__) + + +/** + * Context for all functions in this plugin. + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Database filename. + */ + char *fn; + + /** + * Statements prepared, we are ready to go if true. + */ + bool ready; + + /** + * Native SQLite database handle. + */ + sqlite3 *dbh; + + /** + * Precompiled SQL to store records. + */ + sqlite3_stmt *store_records; + + /** + * Precompiled SQL to deltete existing records. + */ + sqlite3_stmt *delete_records; + + /** + * Precompiled SQL for iterate records within a zone. + */ + sqlite3_stmt *iterate_zone; + + /** + * Precompiled SQL for iterate all records within all zones. + */ + sqlite3_stmt *iterate_all_zones; + + /** + * Precompiled SQL to for reverse lookup based on PKEY. + */ + sqlite3_stmt *zone_to_name; + + /** + * Precompiled SQL to lookup records based on label. + */ + sqlite3_stmt *lookup_label; +}; + + +/** + * Initialize the database connections and associated + * data structures (create tables and indices + * as needed as well). + * + * @param plugin the plugin context (state for this module) + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +database_prepare (struct Plugin *plugin) +{ + if (plugin->ready) + return GNUNET_OK; + struct GNUNET_SQ_ExecuteStatement es[] = { + GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"), + GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"), + GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"), + GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"), + GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""), + GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=NORMAL"), + GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"), + GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"), + GNUNET_SQ_EXECUTE_STATEMENT_END + }; + struct GNUNET_SQ_PrepareStatement ps[] = { + GNUNET_SQ_make_prepare ("INSERT INTO ns098records " + "(zone_private_key,pkey,rvalue,record_count,record_data,label)" + " VALUES (?, ?, ?, ?, ?, ?)", + &plugin->store_records), + GNUNET_SQ_make_prepare ("DELETE FROM ns098records " + "WHERE zone_private_key=? AND label=?", + &plugin->delete_records), + GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label" + " FROM ns098records" + " WHERE zone_private_key=? AND pkey=?", + &plugin->zone_to_name), + GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label" + " FROM ns098records" + " WHERE zone_private_key=? AND uid > ?" + " ORDER BY uid ASC" + " LIMIT ?", + &plugin->iterate_zone), + GNUNET_SQ_make_prepare ( + "SELECT uid,record_count,record_data,label,zone_private_key" + " FROM ns098records" + " WHERE uid > ?" + " ORDER BY uid ASC" + " LIMIT ?", + &plugin->iterate_all_zones), + GNUNET_SQ_make_prepare ("SELECT uid,record_count,record_data,label" + " FROM ns098records" + " WHERE zone_private_key=? AND label=?", + &plugin->lookup_label), + GNUNET_SQ_PREPARE_END + }; + + if (GNUNET_OK != + GNUNET_SQ_exec_statements (plugin->dbh, + es)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to setup database with: `%s'\n"), + sqlite3_errmsg (plugin->dbh)); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_SQ_prepare (plugin->dbh, + ps)) + { + GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to setup database with: `%s'\n"), + sqlite3_errmsg (plugin->dbh)); + return GNUNET_SYSERR; + } + plugin->ready = GNUNET_YES; + return GNUNET_OK; +} + + +/** + * Shutdown database connection and associate data + * structures. + * @param plugin the plugin context (state for this module) + */ +static void +database_shutdown (struct Plugin *plugin) +{ + int result; + sqlite3_stmt *stmt; + + if (NULL != plugin->store_records) + sqlite3_finalize (plugin->store_records); + if (NULL != plugin->delete_records) + sqlite3_finalize (plugin->delete_records); + if (NULL != plugin->iterate_zone) + sqlite3_finalize (plugin->iterate_zone); + if (NULL != plugin->iterate_all_zones) + sqlite3_finalize (plugin->iterate_all_zones); + if (NULL != plugin->zone_to_name) + sqlite3_finalize (plugin->zone_to_name); + if (NULL != plugin->lookup_label) + sqlite3_finalize (plugin->lookup_label); + result = sqlite3_close (plugin->dbh); + if (result == SQLITE_BUSY) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _ ( + "Tried to close sqlite without finalizing all prepared statements.\n")); + stmt = sqlite3_next_stmt (plugin->dbh, + NULL); + while (NULL != stmt) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", + "Closing statement %p\n", + stmt); + result = sqlite3_finalize (stmt); + if (result != SQLITE_OK) + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "sqlite", + "Failed to close statement %p: %d\n", + stmt, + result); + stmt = sqlite3_next_stmt (plugin->dbh, + NULL); + } + result = sqlite3_close (plugin->dbh); + } + if (SQLITE_OK != result) + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "sqlite3_close"); + +} + + +/** + * Store a record in the datastore. Removes any existing record in the + * same zone with the same name. + * + * @param cls closure (internal context for the plugin) + * @param zone_key private key of the zone + * @param label name that is being mapped (at most 255 characters long) + * @param rd_count number of entries in @a rd array + * @param rd array of records with data to store + * @return #GNUNET_OK on success, else #GNUNET_SYSERR + */ +static enum GNUNET_GenericReturnValue +namestore_sqlite_store_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone_key, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct Plugin *plugin = cls; + int n; + struct GNUNET_CRYPTO_PublicKey pkey; + uint64_t rvalue; + ssize_t data_size; + + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + memset (&pkey, + 0, + sizeof(pkey)); + for (unsigned int i = 0; i < rd_count; i++) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Checking if `%d' is zonekey type\n", + rd[i].record_type); + + if (GNUNET_YES == GNUNET_GNSRECORD_is_zonekey_type (rd[i].record_type)) + { + GNUNET_break (GNUNET_YES == + GNUNET_GNSRECORD_identity_from_data (rd[i].data, + rd[i].data_size, + rd[i].record_type, + &pkey)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Storing delegation zone record value `%s'\n", + GNUNET_GNSRECORD_z2s (&pkey)); + + break; + } + } + rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); + data_size = GNUNET_GNSRECORD_records_get_size (rd_count, + rd); + if (data_size < 0) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (data_size > 64 * 65536) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + { + /* First delete 'old' records */ + char data[data_size]; + struct GNUNET_SQ_QueryParam dparams[] = { + GNUNET_SQ_query_param_auto_from_type (zone_key), + GNUNET_SQ_query_param_string (label), + GNUNET_SQ_query_param_end + }; + ssize_t ret; + + ret = GNUNET_GNSRECORD_records_serialize (rd_count, + rd, + data_size, + data); + if ((ret < 0) || + (data_size != ret)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->delete_records, + dparams)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_XXXX"); + GNUNET_SQ_reset (plugin->dbh, + plugin->delete_records); + return GNUNET_SYSERR; + } + n = sqlite3_step (plugin->delete_records); + GNUNET_SQ_reset (plugin->dbh, + plugin->delete_records); + + if (0 != rd_count) + { + uint32_t rd_count32 = (uint32_t) rd_count; + struct GNUNET_SQ_QueryParam sparams[] = { + GNUNET_SQ_query_param_auto_from_type (zone_key), + GNUNET_SQ_query_param_auto_from_type (&pkey), + GNUNET_SQ_query_param_uint64 (&rvalue), + GNUNET_SQ_query_param_uint32 (&rd_count32), + GNUNET_SQ_query_param_fixed_size (data, data_size), + GNUNET_SQ_query_param_string (label), + GNUNET_SQ_query_param_end + }; + + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->store_records, + sparams)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_XXXX"); + GNUNET_SQ_reset (plugin->dbh, + plugin->store_records); + return GNUNET_SYSERR; + } + n = sqlite3_step (plugin->store_records); + GNUNET_SQ_reset (plugin->dbh, + plugin->store_records); + } + } + switch (n) + { + case SQLITE_DONE: + if (0 != rd_count) + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", + "Record stored\n"); + else + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", + "Record deleted\n"); + return GNUNET_OK; + + case SQLITE_BUSY: + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + return GNUNET_NO; + + default: + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + return GNUNET_SYSERR; + } +} + + +/** + * The given 'sqlite' statement has been prepared to be run. + * It will return a record which should be given to the iterator. + * Runs the statement and parses the returned record. + * + * @param plugin plugin context + * @param stmt to run (and then clean up) + * @param zone_key private key of the zone + * @param limit maximum number of results to fetch + * @param iter iterator to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error + */ +static enum GNUNET_GenericReturnValue +get_records_and_call_iterator (struct Plugin *plugin, + sqlite3_stmt *stmt, + const struct + GNUNET_CRYPTO_PrivateKey *zone_key, + uint64_t limit, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + int ret; + int sret; + + ret = GNUNET_OK; + for (uint64_t i = 0; i < limit; i++) + { + sret = sqlite3_step (stmt); + + if (SQLITE_DONE == sret) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Iteration done (no results)\n"); + ret = GNUNET_NO; + break; + } + if (SQLITE_ROW != sret) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "sqlite_step"); + ret = GNUNET_SYSERR; + break; + } + + { + uint64_t seq; + uint32_t record_count; + size_t data_size; + void *data; + char *label; + struct GNUNET_CRYPTO_PrivateKey zk; + struct GNUNET_SQ_ResultSpec rs[] = { + GNUNET_SQ_result_spec_uint64 (&seq), + GNUNET_SQ_result_spec_uint32 (&record_count), + GNUNET_SQ_result_spec_variable_size (&data, + &data_size), + GNUNET_SQ_result_spec_string (&label), + GNUNET_SQ_result_spec_end + }; + struct GNUNET_SQ_ResultSpec rsx[] = { + GNUNET_SQ_result_spec_uint64 (&seq), + GNUNET_SQ_result_spec_uint32 (&record_count), + GNUNET_SQ_result_spec_variable_size (&data, + &data_size), + GNUNET_SQ_result_spec_string (&label), + GNUNET_SQ_result_spec_auto_from_type (&zk), + GNUNET_SQ_result_spec_end + }; + + ret = GNUNET_SQ_extract_result (stmt, + (NULL == zone_key) + ? rsx + : rs); + if ((GNUNET_OK != ret) || + (record_count > 64 * 1024)) + { + /* sanity check, don't stack allocate far too much just + because database might contain a large value here */ + GNUNET_break (0); + ret = GNUNET_SYSERR; + break; + } + else + { + struct GNUNET_GNSRECORD_Data rd[record_count]; + + GNUNET_assert (0 != seq); + if (GNUNET_OK != + GNUNET_GNSRECORD_records_deserialize (data_size, + data, + record_count, + rd)) + { + GNUNET_break (0); + ret = GNUNET_SYSERR; + break; + } + else + { + if (NULL != zone_key) + zk = *zone_key; + if (NULL != iter) + iter (iter_cls, + seq, + &zk, + label, + record_count, + rd); + } + } + GNUNET_SQ_cleanup_result (rs); + } + } + GNUNET_SQ_reset (plugin->dbh, + stmt); + return ret; +} + + +/** + * Lookup records in the datastore for which we are the authority. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone + * @param label name of the record in the zone + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR + */ +static enum GNUNET_GenericReturnValue +namestore_sqlite_lookup_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_auto_from_type (zone), + GNUNET_SQ_query_param_string (label), + GNUNET_SQ_query_param_end + }; + + if (NULL == zone) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->lookup_label, + params)) + { + LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_XXXX"); + GNUNET_SQ_reset (plugin->dbh, + plugin->lookup_label); + return GNUNET_SYSERR; + } + return get_records_and_call_iterator (plugin, + plugin->lookup_label, + zone, + 1, + iter, + iter_cls); +} + + +/** + * Iterate over the results for a particular key and zone in the + * datastore. Will return at most one result to the iterator. + * + * @param cls closure (internal context for the plugin) + * @param zone hash of public key of the zone, NULL to iterate over all zones + * @param serial serial number to exclude in the list of all matching records + * @param limit maximum number of results to return + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error + */ +static enum GNUNET_GenericReturnValue +namestore_sqlite_iterate_records (void *cls, + const struct + GNUNET_CRYPTO_PrivateKey *zone, + uint64_t serial, + uint64_t limit, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + sqlite3_stmt *stmt; + int err; + + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + if (NULL == zone) + { + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_uint64 (&serial), + GNUNET_SQ_query_param_uint64 (&limit), + GNUNET_SQ_query_param_end + }; + + stmt = plugin->iterate_all_zones; + err = GNUNET_SQ_bind (stmt, + params); + } + else + { + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_auto_from_type (zone), + GNUNET_SQ_query_param_uint64 (&serial), + GNUNET_SQ_query_param_uint64 (&limit), + GNUNET_SQ_query_param_end + }; + + stmt = plugin->iterate_zone; + err = GNUNET_SQ_bind (stmt, + params); + } + if (GNUNET_OK != err) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_XXXX"); + GNUNET_SQ_reset (plugin->dbh, + stmt); + return GNUNET_SYSERR; + } + return get_records_and_call_iterator (plugin, + stmt, + zone, + limit, + iter, + iter_cls); +} + + +/** + * Look for an existing PKEY delegation record for a given public key. + * Returns at most one result to the iterator. + * + * @param cls closure (internal context for the plugin) + * @param zone private key of the zone to look up in, never NULL + * @param value_zone public key of the target zone (value), never NULL + * @param iter function to call with the result + * @param iter_cls closure for @a iter + * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error + */ +static enum GNUNET_GenericReturnValue +namestore_sqlite_zone_to_name (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const struct + GNUNET_CRYPTO_PublicKey *value_zone, + GNUNET_NAMESTORE_RecordIterator iter, + void *iter_cls) +{ + struct Plugin *plugin = cls; + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_auto_from_type (zone), + GNUNET_SQ_query_param_auto_from_type (value_zone), + GNUNET_SQ_query_param_end + }; + + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->zone_to_name, + params)) + { + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_XXXX"); + GNUNET_SQ_reset (plugin->dbh, + plugin->zone_to_name); + return GNUNET_SYSERR; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Performing reverse lookup for `%s'\n", + GNUNET_GNSRECORD_z2s (value_zone)); + return get_records_and_call_iterator (plugin, + plugin->zone_to_name, + zone, + 1, + iter, + iter_cls); +} + + +/** + * Begin a transaction for a client. + * This locks the database. SQLite is unable to discern between different + * rows with a specific zone key but the API looks like this anyway. + * https://www.sqlite.org/lang_transaction.html + * + * @param cls closure (internal context for the plugin) + * @param emsg error message set of return code is #GNUNET_SYSERR + * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. + */ +static enum GNUNET_GenericReturnValue +namestore_sqlite_transaction_begin (void *cls, + char **emsg) +{ + struct Plugin *plugin = cls; + int rc; + char *sqlEmsg; + + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + rc = sqlite3_exec (plugin->dbh, "BEGIN IMMEDIATE TRANSACTION;", + NULL, NULL, &sqlEmsg); + if (SQLITE_OK != rc) + { + *emsg = GNUNET_strdup (sqlEmsg); + sqlite3_free (sqlEmsg); + } + return (SQLITE_OK != rc) ? GNUNET_SYSERR : GNUNET_YES; +} + + +/** + * Commit a transaction for a client. + * This releases the lock on the database. + * + * @param cls closure (internal context for the plugin) + * @param emsg error message set of return code is #GNUNET_SYSERR + * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. + */ +static enum GNUNET_GenericReturnValue +namestore_sqlite_transaction_rollback (void *cls, + char **emsg) +{ + struct Plugin *plugin = cls; + int rc; + char *sqlEmsg; + + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + rc = sqlite3_exec (plugin->dbh, "ROLLBACK;", + NULL, NULL, &sqlEmsg); + if (SQLITE_OK != rc) + { + *emsg = GNUNET_strdup (sqlEmsg); + sqlite3_free (sqlEmsg); + } + return (SQLITE_OK != rc) ? GNUNET_SYSERR : GNUNET_YES; +} + + +/** + * Roll back a transaction for a client. + * This releases the lock on the database. + * + * @param cls closure (internal context for the plugin) + * @param emsg error message set of return code is #GNUNET_SYSERR + * @return #GNUNET_YES on success, #GNUNET_SYSERR if transaction cannot be started. + */ +static enum GNUNET_GenericReturnValue +namestore_sqlite_transaction_commit (void *cls, + char **emsg) +{ + struct Plugin *plugin = cls; + int rc; + char *sqlEmsg; + + GNUNET_assert (GNUNET_OK == database_prepare (plugin)); + rc = sqlite3_exec (plugin->dbh, "END TRANSACTION;", + NULL, NULL, &sqlEmsg); + if (SQLITE_OK != rc) + { + *emsg = GNUNET_strdup (sqlEmsg); + sqlite3_free (sqlEmsg); + } + return (SQLITE_OK != rc) ? GNUNET_SYSERR : GNUNET_YES; +} + + +static enum GNUNET_GenericReturnValue +namestore_sqlite_create_tables (void *cls) +{ + struct Plugin *plugin = cls; + struct GNUNET_SQ_ExecuteStatement es[] = { + GNUNET_SQ_make_try_execute ("PRAGMA temp_store=MEMORY"), + GNUNET_SQ_make_try_execute ("PRAGMA synchronous=NORMAL"), + GNUNET_SQ_make_try_execute ("PRAGMA legacy_file_format=OFF"), + GNUNET_SQ_make_try_execute ("PRAGMA auto_vacuum=INCREMENTAL"), + GNUNET_SQ_make_try_execute ("PRAGMA encoding=\"UTF-8\""), + GNUNET_SQ_make_try_execute ("PRAGMA locking_mode=NORMAL"), + GNUNET_SQ_make_try_execute ("PRAGMA journal_mode=WAL"), + GNUNET_SQ_make_try_execute ("PRAGMA page_size=4092"), + GNUNET_SQ_make_execute ("CREATE TABLE IF NOT EXISTS ns098records (" + " uid INTEGER PRIMARY KEY," + " zone_private_key BLOB NOT NULL," + " pkey BLOB," + " rvalue INT8 NOT NULL," + " record_count INT NOT NULL," + " record_data BLOB NOT NULL," + " label TEXT NOT NULL" + ")"), + GNUNET_SQ_make_try_execute ("CREATE INDEX ir_pkey_reverse " + "ON ns098records (zone_private_key,pkey)"), + GNUNET_SQ_make_try_execute ("CREATE INDEX ir_pkey_iter " + "ON ns098records (zone_private_key,uid)"), + GNUNET_SQ_EXECUTE_STATEMENT_END + }; + + if (GNUNET_OK != + GNUNET_SQ_exec_statements (plugin->dbh, + es)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to setup database with: `%s'\n", + sqlite3_errmsg (plugin->dbh)); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +static enum GNUNET_GenericReturnValue +namestore_sqlite_drop_tables (void *cls) +{ + struct Plugin *plugin = cls; + struct GNUNET_SQ_ExecuteStatement es_drop[] = { + GNUNET_SQ_make_execute ("DROP TABLE IF EXISTS ns098records"), + GNUNET_SQ_EXECUTE_STATEMENT_END + }; + + if (GNUNET_OK != + GNUNET_SQ_exec_statements (plugin->dbh, + es_drop)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to drop database with: `%s'\n", + sqlite3_errmsg (plugin->dbh)); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Initialize the database connections and associated + * data structures (create tables and indices + * as needed as well). + * + * @param plugin the plugin context (state for this module) + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +database_connect (struct Plugin *plugin) +{ + char *sqlite_filename; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, + "namestore-sqlite", + "FILENAME", + &sqlite_filename)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "namestore-sqlite", + "FILENAME"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_DISK_file_test (sqlite_filename)) + { + if (GNUNET_OK != + GNUNET_DISK_directory_create_for_file (sqlite_filename)) + { + GNUNET_break (0); + GNUNET_free (sqlite_filename); + return GNUNET_SYSERR; + } + } + + /* Open database and precompile statements */ + if ((NULL == plugin->dbh) && + (SQLITE_OK != sqlite3_open (sqlite_filename, + &plugin->dbh))) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to initialize SQLite: %s.\n"), + sqlite3_errmsg (plugin->dbh)); + GNUNET_free (sqlite_filename); + return GNUNET_SYSERR; + } + GNUNET_free (sqlite_filename); + GNUNET_break (SQLITE_OK == + sqlite3_busy_timeout (plugin->dbh, + BUSY_TIMEOUT_MS)); + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg, + "namestore-sqlite", + "INIT_ON_CONNECT")) + { + if (GNUNET_OK != + namestore_sqlite_create_tables (plugin)) + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Entry point for the plugin. + * + * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*" + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_namestore_sqlite_init (void *cls) +{ + struct Plugin *plugin; + const struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct GNUNET_NAMESTORE_PluginFunctions *api; + + plugin = GNUNET_new (struct Plugin); + plugin->cfg = cfg; + if (GNUNET_OK != database_connect (plugin)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Database could not be connected to.\n"); + GNUNET_free (plugin); + return NULL; + } + api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions); + api->cls = plugin; + api->store_records = &namestore_sqlite_store_records; + api->iterate_records = &namestore_sqlite_iterate_records; + api->zone_to_name = &namestore_sqlite_zone_to_name; + api->lookup_records = &namestore_sqlite_lookup_records; + api->transaction_begin = &namestore_sqlite_transaction_begin; + api->transaction_commit = &namestore_sqlite_transaction_commit; + api->transaction_rollback = &namestore_sqlite_transaction_rollback; + api->create_tables = &namestore_sqlite_create_tables; + api->drop_tables = &namestore_sqlite_drop_tables; + /** + * NOTE: Since SQlite does not support SELECT ... FOR UPDATE this is + * just an alias to lookup_records. The BEGIN IMMEDIATE mechanic currently + * implicitly ensures this API behaves as it should + */ + api->edit_records = &namestore_sqlite_lookup_records; + LOG (GNUNET_ERROR_TYPE_DEBUG, + _ ("SQlite database running\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_namestore_sqlite_done (void *cls) +{ + struct GNUNET_NAMESTORE_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + database_shutdown (plugin); + plugin->cfg = NULL; + GNUNET_free (plugin); + GNUNET_free (api); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "SQlite plugin is finished\n"); + return NULL; +} + + +/* end of plugin_namestore_sqlite.c */ diff --git a/src/plugin/namestore/plugin_rest_namestore.c b/src/plugin/namestore/plugin_rest_namestore.c new file mode 100644 index 000000000..31e78e6dd --- /dev/null +++ b/src/plugin/namestore/plugin_rest_namestore.c @@ -0,0 +1,1364 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Martin Schanzenbach + * @author Philippe Buschmann + * @file namestore/plugin_rest_namestore.c + * @brief GNUnet Namestore REST plugin + */ + +#include "platform.h" +#include "gnunet_error_codes.h" +#include "gnunet_rest_plugin.h" +#include "gnunet_gns_service.h" +#include "gnunet_namestore_service.h" +#include "gnunet_identity_service.h" +#include "gnunet_rest_lib.h" +#include "gnunet_gnsrecord_json_lib.h" +#include "microhttpd.h" +#include + +/** + * Namestore namespace + */ +#define GNUNET_REST_API_NS_NAMESTORE "/namestore" + +/** + * Namestore import API namespace + */ +#define GNUNET_REST_API_NS_NAMESTORE_IMPORT "/namestore/import" + +/** + * State while collecting all egos + */ +#define ID_REST_STATE_INIT 0 + +/** + * Done collecting egos + */ +#define ID_REST_STATE_POST_INIT 1 +/** + * The configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * HTTP methods allows for this plugin + */ +static char *allow_methods; + +/** + * Ego list + */ +static struct EgoEntry *ego_head; + +/** + * Ego list + */ +static struct EgoEntry *ego_tail; + +/** + * The processing state + */ +static int state; + +/** + * Handle to NAMESTORE + */ +static struct GNUNET_NAMESTORE_Handle *ns_handle; + +/** + * Handle to Identity service. + */ +static struct GNUNET_IDENTITY_Handle *identity_handle; + +/** + * @brief struct returned by the initialization function of the plugin + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + +/** + * The default namestore ego + */ +struct EgoEntry +{ + /** + * DLL + */ + struct EgoEntry *next; + + /** + * DLL + */ + struct EgoEntry *prev; + + /** + * Ego Identifier + */ + char *identifier; + + /** + * Public key string + */ + char *keystring; + + /** + * The Ego + */ + struct GNUNET_IDENTITY_Ego *ego; +}; + + +enum UpdateStrategy +{ + UPDATE_STRATEGY_REPLACE, + UPDATE_STRATEGY_APPEND +}; + +/** + * The request handle + */ +struct RequestHandle +{ + /** + * DLL + */ + struct RequestHandle *next; + + /** + * DLL + */ + struct RequestHandle *prev; + + /** + * Records to store + */ + char *record_name; + + /** + * Record type filter + */ + uint32_t record_type; + + /** + * How to update the record set + */ + enum UpdateStrategy update_strategy; + + /** + * Records to store + */ + struct GNUNET_GNSRECORD_Data *rd; + + /** + * Number of records in rd + */ + unsigned int rd_count; + + /** + * RecordInfo array + */ + struct GNUNET_NAMESTORE_RecordInfo *ri; + + /** + * Size of record info + */ + unsigned int rd_set_count; + + /** + * Position of record info + */ + unsigned int rd_set_pos; + + /** + * NAMESTORE Operation + */ + struct GNUNET_NAMESTORE_QueueEntry *ns_qe; + + /** + * For bulk import, we need a dedicated Namestore handle + */ + struct GNUNET_NAMESTORE_Handle *nc; + + /** + * Response object + */ + json_t *resp_object; + + + /** + * Handle to NAMESTORE it + */ + struct GNUNET_NAMESTORE_ZoneIterator *list_it; + + /** + * Private key for the zone + */ + const struct GNUNET_CRYPTO_PrivateKey *zone_pkey; + + /** + * IDENTITY Operation + */ + struct EgoEntry *ego_entry; + + /** + * IDENTITY Operation + */ + struct GNUNET_IDENTITY_Operation *op; + + /** + * Rest connection + */ + struct GNUNET_REST_RequestHandle *rest_handle; + + /** + * Desired timeout for the lookup (default is no timeout). + */ + struct GNUNET_TIME_Relative timeout; + + /** + * ID of a task associated with the resolution process. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * The plugin result processor + */ + GNUNET_REST_ResultProcessor proc; + + /** + * The closure of the result processor + */ + void *proc_cls; + + /** + * The url + */ + char *url; + + /** + * Error code + */ + enum GNUNET_ErrorCode ec; + +}; + +/** + * DLL + */ +static struct RequestHandle *requests_head; + +/** + * DLL + */ +static struct RequestHandle *requests_tail; + + +/** + * Cleanup lookup handle + * @param cls Handle to clean up + */ +static void +cleanup_handle (void *cls) +{ + struct RequestHandle *handle = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n"); + if (NULL != handle->timeout_task) + { + GNUNET_SCHEDULER_cancel (handle->timeout_task); + handle->timeout_task = NULL; + } + if (NULL != handle->record_name) + GNUNET_free (handle->record_name); + if (NULL != handle->url) + GNUNET_free (handle->url); + if (NULL != handle->rd) + { + for (int i = 0; i < handle->rd_count; i++) + { + if (NULL != handle->rd[i].data) + GNUNET_free_nz ((void *) handle->rd[i].data); + } + GNUNET_free (handle->rd); + } + if (NULL != handle->timeout_task) + GNUNET_SCHEDULER_cancel (handle->timeout_task); + if (NULL != handle->list_it) + GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it); + if (NULL != handle->ns_qe) + GNUNET_NAMESTORE_cancel (handle->ns_qe); + if (NULL != handle->nc) + GNUNET_NAMESTORE_disconnect (handle->nc); + if (NULL != handle->resp_object) + { + json_decref (handle->resp_object); + } + GNUNET_CONTAINER_DLL_remove (requests_head, + requests_tail, + handle); + GNUNET_free (handle); +} + + +/** + * Task run on errors. Reports an error and cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + json_t *json_error = json_object (); + char *response; + const char* emsg; + int response_code; + + emsg = GNUNET_ErrorCode_get_hint (handle->ec); + json_object_set_new (json_error, "error", json_string (emsg)); + json_object_set_new (json_error, "error_code", json_integer (handle->ec)); + response_code = GNUNET_ErrorCode_get_http_status (handle->ec); + if (0 == response_code) + response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + response = json_dumps (json_error, 0); + resp = GNUNET_REST_create_response (response); + GNUNET_assert (MHD_YES == + MHD_add_response_header (resp, "Content-Type", + "application/json")); + handle->proc (handle->proc_cls, resp, response_code); + json_decref (json_error); + GNUNET_free (response); + cleanup_handle (handle); +} + + +/** + * Get EgoEntry from list with either a public key or a name + * If public key and name are not NULL, it returns the public key result first + * + * @param handle the RequestHandle + * @param pubkey the public key of an identity (only one can be NULL) + * @param name the name of an identity (only one can be NULL) + * @return EgoEntry or NULL if not found + */ +struct EgoEntry * +get_egoentry_namestore (struct RequestHandle *handle, char *name) +{ + struct EgoEntry *ego_entry; + char *copy = GNUNET_strdup (name); + char *tmp; + + if (NULL == name) + return NULL; + tmp = strtok (copy, "/"); + if (NULL == tmp) + return NULL; + for (ego_entry = ego_head; NULL != ego_entry; + ego_entry = ego_entry->next) + { + if (0 != strcasecmp (tmp, ego_entry->identifier)) + continue; + GNUNET_free (copy); + return ego_entry; + } + GNUNET_free (copy); + return NULL; +} + + +/** + * Does internal server error when iteration failed. + * + * @param cls the `struct RequestHandle` + */ +static void +namestore_iteration_error (void *cls) +{ + struct RequestHandle *handle = cls; + + handle->ec = GNUNET_EC_NAMESTORE_ITERATION_FAILED; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; +} + + +static void +create_finished (void *cls, enum GNUNET_ErrorCode ec) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + + handle->ns_qe = NULL; + handle->ec = ec; + if (GNUNET_EC_NONE != ec) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); +} + + +static void +del_finished (void *cls, enum GNUNET_ErrorCode ec) +{ + struct RequestHandle *handle = cls; + + handle->ns_qe = NULL; + handle->ec = ec; + if (GNUNET_EC_NONE != ec) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->proc (handle->proc_cls, + GNUNET_REST_create_response (NULL), + MHD_HTTP_NO_CONTENT); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); +} + + +/** + * Iteration over all results finished, build final + * response. + * + * @param cls the `struct RequestHandle` + */ +static void +namestore_list_finished (void *cls) +{ + struct RequestHandle *handle = cls; + char *result_str; + struct MHD_Response *resp; + + handle->list_it = NULL; + + if (NULL == handle->resp_object) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_EMPTY; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + result_str = json_dumps (handle->resp_object, 0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); + resp = GNUNET_REST_create_response (result_str); + GNUNET_assert (MHD_YES == + MHD_add_response_header (resp, "Content-Type", + "application/json")); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free (result_str); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); +} + + +/** + * Create a response with requested records + * + * @param handle the RequestHandle + */ +static void +namestore_list_iteration (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd, + struct GNUNET_TIME_Absolute expiry) +{ + struct RequestHandle *handle = cls; + struct GNUNET_GNSRECORD_Data rd_filtered[rd_len]; + json_t *record_obj; + int i = 0; + int j = 0; + + if (rd_len == 0) + { + /** skip **/ + GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1); + return; + } + + for (i = 0; i < rd_len; i++) + { + if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) && + (rd[i].record_type != handle->record_type)) + continue; /* Apply filter */ + rd_filtered[j] = rd[i]; + rd_filtered[j].data = rd[i].data; + j++; + } + /** Only add if not empty **/ + if (j > 0) + { + if (NULL == handle->resp_object) + handle->resp_object = json_array (); + record_obj = GNUNET_GNSRECORD_JSON_from_gnsrecord (rname, + rd_filtered, + j); + json_array_append_new (handle->resp_object, record_obj); + } + GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1); +} + + +/** + * Handle lookup error + * + * @param cls the request handle + */ +static void +ns_lookup_error_cb (void *cls) +{ + struct RequestHandle *handle = cls; + + handle->ec = GNUNET_EC_NAMESTORE_LOOKUP_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, handle); +} + + +static void +ns_get_lookup_cb (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + struct GNUNET_GNSRECORD_Data rd_filtered[rd_len]; + int i = 0; + int j = 0; + + handle->ns_qe = NULL; + for (i = 0; i < rd_len; i++) + { + if ((GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) && + (rd[i].record_type != handle->record_type)) + continue; /* Apply filter */ + rd_filtered[j] = rd[i]; + rd_filtered[j].data = rd[i].data; + j++; + } + /** Return 404 if no set was found **/ + if (j == 0) + { + handle->ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->resp_object = GNUNET_GNSRECORD_JSON_from_gnsrecord (label, + rd_filtered, + j); + GNUNET_SCHEDULER_add_now (&namestore_list_finished, handle); +} + + +/** + * Handle namestore GET request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_get (struct GNUNET_REST_RequestHandle *con_handle, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + struct GNUNET_HashCode key; + enum GNUNET_GNSRECORD_Filter filter_flags; + char *egoname; + char *labelname; + char *typename; + char *boolstring; + + egoname = NULL; + ego_entry = NULL; + + // set zone to name if given + if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url)) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1]; + ego_entry = get_egoentry_namestore (handle, egoname); + if (NULL == ego_entry) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + + GNUNET_CRYPTO_hash ("record_type", strlen ("record_type"), &key); + handle->record_type = GNUNET_GNSRECORD_TYPE_ANY; + if (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key)) + { + typename = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, + &key); + if (NULL != typename) + handle->record_type = GNUNET_GNSRECORD_typename_to_number (typename); + } + GNUNET_CRYPTO_hash ("omit_private", strlen ("omit_private"), &key); + filter_flags = GNUNET_GNSRECORD_FILTER_NONE; + if (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key)) + { + boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, + &key); + if ((0 == strcmp (boolstring, "1")) || + (0 == strcmp (boolstring, "yes")) || + (0 == strcmp (boolstring, "true"))) + filter_flags = GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE; + } + GNUNET_CRYPTO_hash ("include_maintenance", strlen ("include_maintenance"), + &key); + if (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map, &key)) + { + boolstring = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, + &key); + if ((0 == strcmp (boolstring, "1")) || + (0 == strcmp (boolstring, "yes")) || + (0 == strcmp (boolstring, "true"))) + filter_flags |= GNUNET_GNSRECORD_FILTER_INCLUDE_MAINTENANCE; + } + labelname = &egoname[strlen (ego_entry->identifier)]; + if (1 >= strlen (labelname)) + { + /* Iterate over all records */ + handle->list_it = + GNUNET_NAMESTORE_zone_iteration_start2 (ns_handle, + handle->zone_pkey, + &namestore_iteration_error, + handle, + &namestore_list_iteration, + handle, + &namestore_list_finished, + handle, + filter_flags); + if (NULL == handle->list_it) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + return; + } + handle->record_name = GNUNET_strdup (labelname + 1); + handle->ns_qe = GNUNET_NAMESTORE_records_lookup2 (ns_handle, + handle->zone_pkey, + handle->record_name, + &ns_lookup_error_cb, + handle, + &ns_get_lookup_cb, + handle, + filter_flags); + if (NULL == handle->ns_qe) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } +} + + +static void +ns_lookup_cb (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + struct GNUNET_GNSRECORD_Data rd_new[rd_count + handle->rd_count]; + int i = 0; + int j = 0; + + if (UPDATE_STRATEGY_APPEND == handle->update_strategy) + { + for (i = 0; i < rd_count; i++) + rd_new[i] = rd[i]; + } + for (j = 0; j < handle->rd_count; j++) + rd_new[i + j] = handle->rd[j]; + handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, + handle->zone_pkey, + handle->record_name, + i + j, + rd_new, + &create_finished, + handle); + if (NULL == handle->ns_qe) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } +} + + +static void +bulk_tx_commit_cb (void *cls, enum GNUNET_ErrorCode ec) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + + handle->ns_qe = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Commit finished (%d)\n", ec); + handle->ec = ec; + if (GNUNET_EC_NONE != ec) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); +} + + +static void +import_next_cb (void *cls, enum GNUNET_ErrorCode ec) +{ + struct RequestHandle *handle = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Import finished (%d)\n", ec); + handle->ns_qe = NULL; + handle->ec = ec; + if (GNUNET_EC_NONE != ec) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + unsigned int remaining = handle->rd_set_count - handle->rd_set_pos; + if (0 == remaining) + { + handle->ns_qe = GNUNET_NAMESTORE_transaction_commit (handle->nc, + &bulk_tx_commit_cb, + handle); + return; + } + unsigned int sent_rds = 0; + // Find the smallest set of records we can send with our message size + // restriction of 16 bit + handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc, + handle->zone_pkey, + remaining, + &handle->ri[handle-> + rd_set_pos], + &sent_rds, + &import_next_cb, + handle); + if ((NULL == handle->ns_qe) && (0 == sent_rds)) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->rd_set_pos += sent_rds; +} + +static void +bulk_tx_start (void *cls, enum GNUNET_ErrorCode ec) +{ + struct RequestHandle *handle = cls; + json_t *data_js; + json_error_t err; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transaction started...\n"); + handle->ec = ec; + if (GNUNET_EC_NONE != ec) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (0 >= handle->rest_handle->data_size) + { + handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + char term_data[handle->rest_handle->data_size + 1]; + term_data[handle->rest_handle->data_size] = '\0'; + GNUNET_memcpy (term_data, + handle->rest_handle->data, + handle->rest_handle->data_size); + data_js = json_loads (term_data, JSON_DECODE_ANY, &err); + if (NULL == data_js) + { + handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error parsing data: %s", err.text); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (! json_is_array (data_js)) + { + handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; + GNUNET_SCHEDULER_add_now (&do_error, handle); + json_decref (data_js); + return; + } + handle->rd_set_count = json_array_size (data_js); + handle->ri = GNUNET_malloc (handle->rd_set_count + * sizeof (struct GNUNET_NAMESTORE_RecordInfo)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got record set of size %d\n", handle->rd_set_count); + char *albl; + size_t index; + json_t *value; + json_array_foreach (data_js, index, value) { + { + struct GNUNET_GNSRECORD_Data *rd; + struct GNUNET_JSON_Specification gnsspec[] = + { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&rd, + &handle->ri[index].a_rd_count, + &albl), + GNUNET_JSON_spec_end () }; + if (GNUNET_OK != GNUNET_JSON_parse (value, gnsspec, NULL, NULL)) + { + handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; + GNUNET_SCHEDULER_add_now (&do_error, handle); + json_decref (data_js); + return; + } + handle->ri[index].a_rd = rd; + handle->ri[index].a_label = albl; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Parsed record set for name %s\n", + handle->ri[index].a_label); + } + } + // json_decref (data_js); + + unsigned int sent_rds = 0; + // Find the smallest set of records we can send with our message size + // restriction of 16 bit + handle->ns_qe = GNUNET_NAMESTORE_records_store2 (handle->nc, + handle->zone_pkey, + handle->rd_set_count, + handle->ri, + &sent_rds, + &import_next_cb, + handle); + if ((NULL == handle->ns_qe) && (0 == sent_rds)) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->rd_set_pos += sent_rds; +} + + +/** + * Handle namestore POST import + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_import (struct GNUNET_REST_RequestHandle *con_handle, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + char *egoname; + + // set zone to name if given + if (strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1 >= strlen ( + handle->url)) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + ego_entry = NULL; + + egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE_IMPORT) + 1]; + ego_entry = get_egoentry_namestore (handle, egoname); + + if (NULL == ego_entry) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + + // We need a per-client connection for a transactional bulk import + handle->nc = GNUNET_NAMESTORE_connect (cfg); + if (NULL == handle->nc) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->ns_qe = GNUNET_NAMESTORE_transaction_begin (handle->nc, + &bulk_tx_start, + handle); +} + +/** + * Handle namestore POST/PUT request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_add_or_update (struct GNUNET_REST_RequestHandle *con_handle, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + char *egoname; + json_t *data_js; + json_error_t err; + + char term_data[handle->rest_handle->data_size + 1]; + + if (0 >= handle->rest_handle->data_size) + { + handle->ec = GNUNET_EC_NAMESTORE_NO_RECORDS_GIVEN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + term_data[handle->rest_handle->data_size] = '\0'; + GNUNET_memcpy (term_data, + handle->rest_handle->data, + handle->rest_handle->data_size); + data_js = json_loads (term_data, JSON_DECODE_ANY, &err); + struct GNUNET_JSON_Specification gnsspec[] = + { GNUNET_GNSRECORD_JSON_spec_gnsrecord (&handle->rd, &handle->rd_count, + &handle->record_name), + GNUNET_JSON_spec_end () }; + if (GNUNET_OK != GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL)) + { + handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; + GNUNET_SCHEDULER_add_now (&do_error, handle); + json_decref (data_js); + return; + } + GNUNET_JSON_parse_free (gnsspec); + if (0 >= strlen (handle->record_name)) + { + handle->ec = GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; + GNUNET_SCHEDULER_add_now (&do_error, handle); + json_decref (data_js); + return; + } + json_decref (data_js); + + egoname = NULL; + ego_entry = NULL; + + // set zone to name if given + if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url)) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1]; + ego_entry = get_egoentry_namestore (handle, egoname); + + if (NULL == ego_entry) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + handle->ns_qe = GNUNET_NAMESTORE_records_lookup (ns_handle, + handle->zone_pkey, + handle->record_name, + &ns_lookup_error_cb, + handle, + &ns_lookup_cb, + handle); + if (NULL == handle->ns_qe) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } +} + + +/** + * Handle namestore PUT request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_update (struct GNUNET_REST_RequestHandle *con_handle, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + handle->update_strategy = UPDATE_STRATEGY_REPLACE; + namestore_add_or_update (con_handle, url, cls); +} + + +/** + * Handle namestore POST request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_add (struct GNUNET_REST_RequestHandle *con_handle, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + handle->update_strategy = UPDATE_STRATEGY_APPEND; + namestore_add_or_update (con_handle, url, cls); +} + + +/** + * Handle namestore DELETE request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_delete (struct GNUNET_REST_RequestHandle *con_handle, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct EgoEntry *ego_entry; + char *egoname; + char *labelname; + + egoname = NULL; + ego_entry = NULL; + + // set zone to name if given + if (strlen (GNUNET_REST_API_NS_NAMESTORE) + 1 >= strlen (handle->url)) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + egoname = &handle->url[strlen (GNUNET_REST_API_NS_NAMESTORE) + 1]; + ego_entry = get_egoentry_namestore (handle, egoname); + if (NULL == ego_entry) + { + handle->ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->zone_pkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); + labelname = &egoname[strlen (ego_entry->identifier)]; + // set zone to name if given + if (1 >= strlen (labelname)) + { + /* label is only "/" */ + handle->ec = GNUNET_EC_NAMESTORE_NO_LABEL_GIVEN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + } + + handle->record_name = GNUNET_strdup (labelname + 1); + handle->ns_qe = GNUNET_NAMESTORE_records_store (ns_handle, + handle->zone_pkey, + handle->record_name, + 0, + NULL, + &del_finished, + handle); + if (NULL == handle->ns_qe) + { + handle->ec = GNUNET_EC_NAMESTORE_UNKNOWN; + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } +} + + +/** + * Respond to OPTIONS request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +options_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char *url, + void *cls) +{ + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + // independent of path return all options + resp = GNUNET_REST_create_response (NULL); + GNUNET_assert (MHD_YES == + MHD_add_response_header (resp, + "Access-Control-Allow-Methods", + allow_methods)); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_SCHEDULER_add_now (&cleanup_handle, handle); + return; +} + + +static void +list_ego (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *identifier) +{ + struct EgoEntry *ego_entry; + struct GNUNET_CRYPTO_PublicKey pk; + + if ((NULL == ego) && (ID_REST_STATE_INIT == state)) + { + state = ID_REST_STATE_POST_INIT; + return; + } + if (NULL == ego) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Called with NULL ego\n"); + return; + } + if (ID_REST_STATE_INIT == state) + { + ego_entry = GNUNET_new (struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk); + ego_entry->ego = ego; + ego_entry->identifier = GNUNET_strdup (identifier); + GNUNET_CONTAINER_DLL_insert_tail (ego_head, + ego_tail, + ego_entry); + } + /* Ego renamed or added */ + if (identifier != NULL) + { + for (ego_entry = ego_head; NULL != ego_entry; + ego_entry = ego_entry->next) + { + if (ego_entry->ego == ego) + { + /* Rename */ + GNUNET_free (ego_entry->identifier); + ego_entry->identifier = GNUNET_strdup (identifier); + break; + } + } + if (NULL == ego_entry) + { + /* Add */ + ego_entry = GNUNET_new (struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->keystring = GNUNET_CRYPTO_public_key_to_string (&pk); + ego_entry->ego = ego; + ego_entry->identifier = GNUNET_strdup (identifier); + GNUNET_CONTAINER_DLL_insert_tail (ego_head, + ego_tail, + ego_entry); + } + } + else + { + /* Delete */ + for (ego_entry = ego_head; NULL != ego_entry; + ego_entry = ego_entry->next) + { + if (ego_entry->ego == ego) + break; + } + if (NULL == ego_entry) + return; /* Not found */ + + GNUNET_CONTAINER_DLL_remove (ego_head, + ego_tail, + ego_entry); + GNUNET_free (ego_entry->identifier); + GNUNET_free (ego_entry->keystring); + GNUNET_free (ego_entry); + return; + } + +} + + +/** + * Function processing the REST call + * + * @param method HTTP method + * @param url URL of the HTTP request + * @param data body of the HTTP request (optional) + * @param data_size length of the body + * @param proc callback function for the result + * @param proc_cls closure for callback function + * @return GNUNET_OK if request accepted + */ +static enum GNUNET_GenericReturnValue +rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle, + GNUNET_REST_ResultProcessor proc, + void *proc_cls) +{ + struct RequestHandle *handle = GNUNET_new (struct RequestHandle); + struct GNUNET_REST_RequestHandlerError err; + static const struct GNUNET_REST_RequestHandler handlers[] = + { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get }, + { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE_IMPORT, + &namestore_import }, + { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add }, + { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_update }, + { MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, + &namestore_delete }, + { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont }, + GNUNET_REST_HANDLER_END }; + + handle->ec = GNUNET_EC_NONE; + handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + handle->proc_cls = proc_cls; + handle->proc = proc; + handle->rest_handle = rest_handle; + handle->zone_pkey = NULL; + handle->timeout_task = + GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_error, handle); + handle->url = GNUNET_strdup (rest_handle->url); + if (handle->url[strlen (handle->url) - 1] == '/') + handle->url[strlen (handle->url) - 1] = '\0'; + GNUNET_CONTAINER_DLL_insert (requests_head, + requests_tail, + handle); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); + if (GNUNET_NO == + GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, + handle)) + { + cleanup_handle (handle); + return GNUNET_NO; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); + return GNUNET_YES; +} + + +/** + * Entry point for the plugin. + * + * @param cls Config info + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_rest_namestore_init (void *cls) +{ + static struct Plugin plugin; + struct GNUNET_REST_Plugin *api; + + cfg = cls; + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof(struct Plugin)); + plugin.cfg = cfg; + api = GNUNET_new (struct GNUNET_REST_Plugin); + api->cls = &plugin; + api->name = GNUNET_REST_API_NS_NAMESTORE; + api->process_request = &rest_process_request; + state = ID_REST_STATE_INIT; + GNUNET_asprintf (&allow_methods, + "%s, %s, %s, %s, %s", + MHD_HTTP_METHOD_GET, + MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, + MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_OPTIONS); + ns_handle = GNUNET_NAMESTORE_connect (cfg); + identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _ ( + "Namestore REST API initialized\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_rest_namestore_done (void *cls) +{ + struct GNUNET_REST_Plugin *api = cls; + struct Plugin *plugin = api->cls; + struct RequestHandle *request; + struct EgoEntry *ego_entry; + struct EgoEntry *ego_tmp; + + plugin->cfg = NULL; + while (NULL != (request = requests_head)) + do_error (request); + if (NULL != identity_handle) + GNUNET_IDENTITY_disconnect (identity_handle); + if (NULL != ns_handle) + GNUNET_NAMESTORE_disconnect (ns_handle); + + for (ego_entry = ego_head; NULL != ego_entry;) + { + ego_tmp = ego_entry; + ego_entry = ego_entry->next; + GNUNET_free (ego_tmp->identifier); + GNUNET_free (ego_tmp->keystring); + GNUNET_free (ego_tmp); + } + + GNUNET_free (allow_methods); + GNUNET_free (api); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Namestore REST plugin is finished\n"); + return NULL; +} + + +/* end of plugin_rest_namestore.c */ diff --git a/src/plugin/namestore/test_common.c b/src/plugin/namestore/test_common.c new file mode 100644 index 000000000..4df24a7f7 --- /dev/null +++ b/src/plugin/namestore/test_common.c @@ -0,0 +1,128 @@ +/* + This file is part of GNUnet. + Copyright (C) 2019 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_common.c + * @brief common functions for testcase setup + */ +#include "platform.h" +#include + +/** + * test if we can load the plugin @a name. + */ +static int +TNC_test_plugin (const char *cfg_name) +{ + char *database; + char *db_lib_name; + struct GNUNET_NAMESTORE_PluginFunctions *db; + struct GNUNET_CONFIGURATION_Handle *cfg; + + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_load (cfg, + cfg_name)) + { + GNUNET_break (0); + GNUNET_CONFIGURATION_destroy (cfg); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "namestore", + "database", + &database)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No database backend configured\n"); + GNUNET_CONFIGURATION_destroy (cfg); + return GNUNET_SYSERR; + } + GNUNET_asprintf (&db_lib_name, + "libgnunet_plugin_namestore_%s", + database); + GNUNET_free (database); + db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); + if (NULL == db) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load plugin `%s'\n", + db_lib_name); + } + else + { + if (GNUNET_OK != db->create_tables (db->cls)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error creating tables\n"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != db->drop_tables (db->cls)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error dropping tables\n"); + return GNUNET_SYSERR; + } + GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db)); + } + GNUNET_free (db_lib_name); + GNUNET_CONFIGURATION_destroy (cfg); + if (NULL == db) + return GNUNET_NO; + return GNUNET_YES; +} + + +/** + * General setup logic for starting the tests. Obtains the @a + * plugin_name and initializes the @a cfg_name. + */ +#define SETUP_CFG2(file_template, plugin_name, cfg_name) \ + do \ + { \ + GNUNET_log_setup (__FILE__, "WARNING", NULL); \ + plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); \ + GNUNET_asprintf (&cfg_name, file_template, plugin_name); \ + if (! TNC_test_plugin (cfg_name)) \ + { \ + GNUNET_free (plugin_name); \ + GNUNET_free (cfg_name); \ + return 77; \ + } \ + GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TEST_HOME"); \ + } while (0) +/** + * General setup logic for starting the tests. Obtains the @a + * plugin_name and initializes the @a cfg_name. + */ +#define SETUP_CFG(plugin_name, cfg_name) \ + do \ + { \ + GNUNET_log_setup (__FILE__, "WARNING", NULL); \ + plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); \ + GNUNET_asprintf (&cfg_name, "test_namestore_api_%s.conf", plugin_name); \ + if (! TNC_test_plugin (cfg_name)) \ + { \ + GNUNET_free (plugin_name); \ + GNUNET_free (cfg_name); \ + return 77; \ + } \ + GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TEST_HOME"); \ + } while (0) diff --git a/src/plugin/namestore/test_hostkey b/src/plugin/namestore/test_hostkey new file mode 100644 index 000000000..e69de29bb diff --git a/src/plugin/namestore/test_namestore_api.conf b/src/plugin/namestore/test_namestore_api.conf new file mode 100644 index 000000000..1648c7cae --- /dev/null +++ b/src/plugin/namestore/test_namestore_api.conf @@ -0,0 +1,43 @@ +@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf +@INLINE@ ../../contrib/conf/gnunet/no_autostart_above_core.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-namestore/ + +[namestore] +DATABASE = sqlite +START_ON_DEMAND = YES +#PREFIX = valgrind --track-origins=yes --log-file=/tmp/ns_tx.log +RETURN_ORPHANED = YES + +[namecache] +DATABASE = sqlite +START_ON_DEMAND = YES + +[zonemaster] +START_ON_DEMAND = YES +IMMEDIATE_START = NO + +[dht] +START_ON_DEMAND = YES +IMMEDIATE_START = NO + + +[zonemaster-monitor] +START_ON_DEMAND = YES +IMMEDIATE_START = YES + +[identity] +START_ON_DEMAND = YES + +[nse] +WORKBITS = 0 + +[rest] +BASIC_AUTH_ENABLED=NO +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/v_log + + + +[transport] +PLUGINS = diff --git a/src/plugin/namestore/test_namestore_api_edit_records.c b/src/plugin/namestore/test_namestore_api_edit_records.c new file mode 100644 index 000000000..a6bce7c17 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_edit_records.c @@ -0,0 +1,399 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_edit_records.c + * @brief testcase for namestore_api.c: Multiple clients work with record set. + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_NAMESTORE_Handle *nsh2; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static int res; + +static int removed; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + +static int nonce = 0; + +static void +cleanup () +{ + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + cleanup (); + res = 1; +} + + +static void +end (void *cls) +{ + cleanup (); + res = 0; +} + +static void +lookup_it (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + GNUNET_assert (0 == rd_count); + GNUNET_SCHEDULER_add_now (&end, NULL); +} + +static void +fail_cb (void *cls) +{ + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; +} + +static void +remove_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + nsqe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to roll back: `%s'\n"), + GNUNET_ErrorCode_get_hint (ec)); + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, + NULL); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rolled back, perform lookup\n"); + removed = GNUNET_YES; + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + GNUNET_SCHEDULER_add_now (&end, NULL); +} + +static void +fail_cb_lock (void *cls); + +static void +edit_cont_b (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + const char *name = cls; + /** + * We should probably never get here right at first. + * We may want to change the blocking of nsh2 so that we do get this + * eventually instead of the error callback above when locked. + */ + if (0 == nonce) + { + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + + } + /* Abort transaction for B */ + nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh2, remove_cont, + (void *) name); +} + + +static void +commit_cont_a (void *cls, + enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + GNUNET_assert (NULL != cls); + nsqe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore could not store record: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + /** + * Try again for B + */ + nsqe = GNUNET_NAMESTORE_records_edit (nsh2, + &privkey, + name, + &fail_cb_lock, + (void *) name, + &edit_cont_b, + (void *) name); + + GNUNET_assert (NULL != nsqe); +} + +static void +fail_cb_lock (void *cls) +{ + const char *name = cls; + if (1 == nonce) + { + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + nonce = 1; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to aquire additional lock\n"); + /* Now, we stop the transaction for B */ + nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont_a, + (void *) name); +} + + +static void +begin_cont_b (void *cls, + enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + GNUNET_assert (GNUNET_EC_NONE == ec); + /** Now, we expect this to "hang" let's see how this behaves in practice. */ + nsqe = GNUNET_NAMESTORE_records_edit (nsh2, + &privkey, + name, + &fail_cb_lock, + (void *) name, + &edit_cont_b, + (void *) name); + + GNUNET_assert (NULL != nsqe); +} + + +static void +edit_cont (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + const char *name = cls; + + GNUNET_assert (1 == rd_count); + /* Now, we start a transaction for B */ + nsqe = GNUNET_NAMESTORE_transaction_begin (nsh2, begin_cont_b, (void *) name); +} + + +static void +begin_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + GNUNET_assert (GNUNET_EC_NONE == ec); + nsqe = GNUNET_NAMESTORE_records_edit (nsh, + &privkey, + name, + &fail_cb, + (void *) name, + &edit_cont, + (void *) name); + + GNUNET_assert (NULL != nsqe); +} + +static void +preload_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + GNUNET_assert (NULL != cls); + nsqe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore could not store record: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + /* We start transaction for A */ + nsqe = GNUNET_NAMESTORE_transaction_begin (nsh, begin_cont, (void *) name); + +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_GNSRECORD_Data rd; + const char *name = "dummy"; + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + nsh = GNUNET_NAMESTORE_connect (cfg); + nsh2 = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + GNUNET_break (NULL != nsh2); + + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_key_get_public (&privkey, + &pubkey); + + removed = GNUNET_NO; + + rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us; + rd.record_type = TEST_RECORD_TYPE; + rd.data_size = TEST_RECORD_DATALEN; + rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); + rd.flags = 0; + memset ((char *) rd.data, + 'a', + TEST_RECORD_DATALEN); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &preload_cont, + (void *) name); + GNUNET_assert (NULL != nsqe); + GNUNET_free_nz ((void *) rd.data); + + /*nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont); + nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh, rollback_cont); Must also happen on disconnect + nsqe = GNUNET_NAMESTORE_records_edit (nsh, + &privkey, + name, + 1, + &rd, + &edit_cont, + (void *) name); + nsqe = GNUNET_NAMESTORE_records_insert_bulk (nsh, + count, + &rd, + & + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &put_cont, + (void *) name);*/ + GNUNET_assert (NULL != nsqe); +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-remove", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_remove.c */ diff --git a/src/plugin/namestore/test_namestore_api_lookup_nick.c b/src/plugin/namestore/test_namestore_api_lookup_nick.c new file mode 100644 index 000000000..21fc1ef79 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_lookup_nick.c @@ -0,0 +1,347 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_lookup_nick.c + * @brief testcase for namestore_api.c: NICK records + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_gns_service.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_DATALEN 123 + +#define TEST_NICK "gnunettestnick" + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static int res; + +static struct GNUNET_GNSRECORD_Data rd_orig; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + +// static const char * name = "dummy.dummy.gnunet"; +static const char *name = "d"; + +static char *record_data; + +static void +cleanup () +{ + GNUNET_free (record_data); + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + * @param tc scheduler context + */ +static void +endbadly (void *cls) +{ + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + cleanup (); + res = 1; +} + + +static void +end (void *cls) +{ + cleanup (); + res = 0; +} + + +static void +lookup_it (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + nsqe = NULL; + int c; + int found_record = GNUNET_NO; + int found_nick = GNUNET_NO; + + if (0 != GNUNET_memcmp (&privkey, zone)) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + if (NULL == label) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + if (0 != strcmp (label, name)) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + if (2 != rd_count) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + for (c = 0; c < rd_count; c++) + { + if (GNUNET_GNSRECORD_TYPE_NICK == rd[c].record_type) + { + if (rd[c].data_size != strlen (TEST_NICK) + 1) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + if (0 != (rd[c].flags & GNUNET_GNSRECORD_RF_PRIVATE)) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + if (0 != strcmp (rd[c].data, TEST_NICK)) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + found_nick = GNUNET_YES; + } + else + { + if (rd[c].record_type != TEST_RECORD_TYPE) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + if (rd[c].data_size != TEST_RECORD_DATALEN) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + if (0 != memcmp (rd[c].data, rd_orig.data, TEST_RECORD_DATALEN)) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + if (rd[c].flags != rd->flags) + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + found_record = GNUNET_YES; + } + } + + /* Done */ + if ((GNUNET_YES == found_nick) && (GNUNET_YES == found_record)) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + GNUNET_SCHEDULER_add_now (&end, NULL); + } + else + { + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + GNUNET_SCHEDULER_add_now (&endbadly, NULL); + } +} + + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); +} + + +static void +put_cont (void *cls, enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + nsqe = NULL; + GNUNET_assert (NULL != cls); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (ec == GNUNET_EC_NONE) ? "SUCCESS" : "FAIL"); + + if (GNUNET_EC_NONE != ec) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + /* Lookup */ + nsqe = GNUNET_NAMESTORE_records_lookup (nsh, + &privkey, + name, + &fail_cb, + NULL, + &lookup_it, + NULL); +} + + +static void +nick_cont (void *cls, enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Nick added : %s\n", + (ec == GNUNET_EC_NONE) ? "SUCCESS" : "FAIL"); + + rd_orig.expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; + rd_orig.record_type = TEST_RECORD_TYPE; + rd_orig.data_size = TEST_RECORD_DATALEN; + record_data = GNUNET_malloc (TEST_RECORD_DATALEN); + rd_orig.data = record_data; + rd_orig.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + memset ((char *) rd_orig.data, 'a', TEST_RECORD_DATALEN); + + nsqe = GNUNET_NAMESTORE_records_store (nsh, &privkey, + name, + 1, + &rd_orig, + &put_cont, (void *) name); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_GNSRECORD_Data rd; + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_key_get_public (&privkey, + &pubkey); + + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + + memset (&rd, 0, sizeof(rd)); + rd.data = TEST_NICK; + rd.data_size = strlen (TEST_NICK) + 1; + rd.record_type = GNUNET_GNSRECORD_TYPE_NICK; + rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; + rd.flags |= GNUNET_GNSRECORD_RF_PRIVATE; + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + GNUNET_GNS_EMPTY_LABEL_AT, + 1, + &rd, + &nick_cont, + (void *) name); + + if (NULL == nsqe) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore cannot store no block\n")); + } +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-lookup-nick", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_store.c */ diff --git a/src/plugin/namestore/test_namestore_api_monitoring.c b/src/plugin/namestore/test_namestore_api_monitoring.c new file mode 100644 index 000000000..74dad3749 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_monitoring.c @@ -0,0 +1,378 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_monitoring.c + * @brief testcase for zone monitoring functionality: monitor first, then add records + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PrivateKey privkey2; + +static struct GNUNET_NAMESTORE_ZoneMonitor *zm; + +static int res; + +static char *s_name_1; + +static struct GNUNET_GNSRECORD_Data *s_rd_1; + +static char *s_name_2; + +static struct GNUNET_GNSRECORD_Data *s_rd_2; + +static char *s_name_3; + +static struct GNUNET_GNSRECORD_Data *s_rd_3; + +struct GNUNET_NAMESTORE_QueueEntry *ns_ops[3]; + + +static void +do_shutdown () +{ + if (NULL != zm) + { + GNUNET_NAMESTORE_zone_monitor_stop (zm); + zm = NULL; + } + if (NULL != ns_ops[0]) + { + GNUNET_NAMESTORE_cancel (ns_ops[0]); + ns_ops[0] = NULL; + } + if (NULL != ns_ops[1]) + { + GNUNET_NAMESTORE_cancel (ns_ops[1]); + ns_ops[1] = NULL; + } + if (NULL != ns_ops[2]) + { + GNUNET_NAMESTORE_cancel (ns_ops[2]); + ns_ops[2] = NULL; + } + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_free (s_name_1); + GNUNET_free (s_name_2); + GNUNET_free (s_name_3); + + if (s_rd_1 != NULL) + { + GNUNET_free_nz ((void *) s_rd_1->data); + GNUNET_free (s_rd_1); + } + if (s_rd_2 != NULL) + { + GNUNET_free_nz ((void *) s_rd_2->data); + GNUNET_free (s_rd_2); + } + if (s_rd_3 != NULL) + { + GNUNET_free_nz ((void *) s_rd_3->data); + GNUNET_free (s_rd_3); + } +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + do_shutdown (); + res = 1; +} + + +static void +end (void *cls) +{ + do_shutdown (); + res = 0; +} + + +static void +zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + static int returned_records; + static int fail = GNUNET_NO; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Comparing results name %s\n", + name); + if (0 != GNUNET_memcmp (zone_key, + &privkey)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Monitoring returned wrong zone key\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + if (0 == strcmp (name, s_name_1)) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) + { + GNUNET_break (0); + fail = GNUNET_YES; + } + } + else if (0 == strcmp (name, s_name_2)) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) + { + GNUNET_break (0); + fail = GNUNET_YES; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid name %s\n", + name); + GNUNET_break (0); + fail = GNUNET_YES; + } + GNUNET_NAMESTORE_zone_monitor_next (zm, + 1); + if (2 == ++returned_records) + { + if (endbadly_task != NULL) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + } + if (GNUNET_YES == fail) + GNUNET_SCHEDULER_add_now (&endbadly, NULL); + else + GNUNET_SCHEDULER_add_now (&end, NULL); + } +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + static int c = 0; + char *label = cls; + + if (0 == strcmp (label, s_name_1)) + ns_ops[0] = NULL; + else if (0 == strcmp (label, s_name_2)) + ns_ops[1] = NULL; + else if (0 == strcmp (label, s_name_3)) + ns_ops[2] = NULL; + + if (GNUNET_EC_NONE == ec) + { + c++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record %u: `%s'\n", + c, + label); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create record `%s'\n", + label); + GNUNET_break (0); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, + NULL); + } +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_new_array (count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int c = 0; c < count; c++) + { + rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd[c].record_type = TEST_RECORD_TYPE; + rd[c].data_size = 50; + rd[c].data = GNUNET_malloc (50); + rd[c].flags = 0; + memset ((char *) rd[c].data, 'a', 50); + } + return rd; +} + + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); +} + + +static void +sync_cb (void *cls) +{ + /* do nothing */ +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + res = 1; + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + /* Start monitoring */ + zm = GNUNET_NAMESTORE_zone_monitor_start (cfg, + &privkey, + GNUNET_YES, + &fail_cb, + NULL, + &zone_proc, + NULL, + &sync_cb, + NULL); + if (NULL == zm) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone monitor\n"); + GNUNET_break (0); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &endbadly, NULL); + /* Connect to namestore */ + nsh = GNUNET_NAMESTORE_connect (cfg); + if (NULL == nsh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Connect to namestore\n"); + GNUNET_break (0); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 3\n"); + /* name in different zone */ + GNUNET_asprintf (&s_name_3, "dummy3"); + s_rd_3 = create_record (1); + GNUNET_assert (NULL != (ns_ops[2] = + GNUNET_NAMESTORE_records_store (nsh, + &privkey2, + s_name_3, + 1, + s_rd_3, + &put_cont, + s_name_3))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 1\n"); + GNUNET_asprintf (&s_name_1, "dummy1"); + s_rd_1 = create_record (1); + GNUNET_assert (NULL != (ns_ops[0] = + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_1, + 1, + s_rd_1, + &put_cont, + s_name_1))); + + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record 2 \n"); + GNUNET_asprintf (&s_name_2, "dummy2"); + s_rd_2 = create_record (1); + GNUNET_assert (NULL != (ns_ops[1] = + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_2, + 1, + s_rd_2, + &put_cont, + s_name_2))); +} + + +#include "test_common.c" + + +int +main (int argc, + char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-monitoring", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_monitoring.c */ diff --git a/src/plugin/namestore/test_namestore_api_monitoring_existing.c b/src/plugin/namestore/test_namestore_api_monitoring_existing.c new file mode 100644 index 000000000..fe17833c8 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_monitoring_existing.c @@ -0,0 +1,393 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_monitoring_existing.c + * @brief testcase for zone monitoring functionality: add records first, then monitor + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) + +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PrivateKey privkey2; + +static struct GNUNET_NAMESTORE_ZoneMonitor *zm; + +static int res; + +static const char *s_name_1; + +static struct GNUNET_GNSRECORD_Data *s_rd_1; + +static const char *s_name_2; + +static struct GNUNET_GNSRECORD_Data *s_rd_2; + +static const char *s_name_3; + +static struct GNUNET_GNSRECORD_Data *s_rd_3; + +struct GNUNET_NAMESTORE_QueueEntry *ns_ops[3]; + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + endbadly_task = NULL; + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + res = 1; +} + + +static void +end (void *cls) +{ + if (NULL != zm) + { + GNUNET_NAMESTORE_zone_monitor_stop (zm); + zm = NULL; + } + if (NULL != ns_ops[0]) + { + GNUNET_NAMESTORE_cancel (ns_ops[0]); + ns_ops[0] = NULL; + } + if (NULL != ns_ops[1]) + { + GNUNET_NAMESTORE_cancel (ns_ops[1]); + ns_ops[1] = NULL; + } + if (NULL != ns_ops[2]) + { + GNUNET_NAMESTORE_cancel (ns_ops[2]); + ns_ops[2] = NULL; + } + if (NULL != endbadly_task) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + } + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + if (NULL != s_rd_1) + { + GNUNET_free_nz ((void *) s_rd_1->data); + GNUNET_free (s_rd_1); + } + if (NULL != s_rd_2) + { + GNUNET_free_nz ((void *) s_rd_2->data); + GNUNET_free (s_rd_2); + } + if (NULL != s_rd_3) + { + GNUNET_free_nz ((void *) s_rd_3->data); + GNUNET_free (s_rd_3); + } +} + + +static void +zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + static int returned_records; + static int fail = GNUNET_NO; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Comparing results name %s\n", + name); + if (0 != GNUNET_memcmp (zone_key, + &privkey)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Monitoring returned wrong zone key\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (0 == strcmp (name, + s_name_1)) + { + if (GNUNET_YES != + GNUNET_GNSRECORD_records_cmp (rd, + s_rd_1)) + { + GNUNET_break (0); + fail = GNUNET_YES; + } + } + else if (0 == strcmp (name, + s_name_2)) + { + if (GNUNET_YES != + GNUNET_GNSRECORD_records_cmp (rd, + s_rd_2)) + { + GNUNET_break (0); + fail = GNUNET_YES; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid name %s\n", + name); + GNUNET_break (0); + fail = GNUNET_YES; + } + GNUNET_NAMESTORE_zone_monitor_next (zm, + 1); + if (2 == ++returned_records) + { + GNUNET_SCHEDULER_shutdown (); + if (GNUNET_YES == fail) + { + GNUNET_break (0); + res = 1; + } + else + { + res = 0; + } + } +} + + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); +} + + +static void +sync_cb (void *cls) +{ + /* do nothing */ +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + static int c = 0; + const char *label = cls; + + if (0 == strcmp (label, + s_name_1)) + ns_ops[0] = NULL; + else if (0 == strcmp (label, + s_name_2)) + ns_ops[1] = NULL; + else if (0 == strcmp (label, + s_name_3)) + ns_ops[2] = NULL; + + if (GNUNET_EC_NONE == ec) + { + c++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record %u: `%s'\n", + c, + label); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to created records\n"); + GNUNET_break (0); + res = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_new_array (count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int c = 0; c < count; c++) + { + rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd[c].record_type = TEST_RECORD_TYPE; + rd[c].data_size = 50; + rd[c].data = GNUNET_malloc (50); + rd[c].flags = 0; + memset ((char *) rd[c].data, + 'a', + 50); + } + return rd; +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *mycfg, + struct GNUNET_TESTING_Peer *peer) +{ + res = 1; + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); + + cfg = mycfg; + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + /* Connect to namestore */ + nsh = GNUNET_NAMESTORE_connect (cfg); + if (NULL == nsh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Connect to namestore failed\n"); + GNUNET_break (0); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, + NULL); + return; + } + /* Start monitoring */ + zm = GNUNET_NAMESTORE_zone_monitor_start (cfg, + &privkey, + GNUNET_YES, + &fail_cb, + NULL, + &zone_proc, + NULL, + &sync_cb, + NULL); + if (NULL == zm) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone monitor\n"); + GNUNET_break (0); + res = 1; + GNUNET_SCHEDULER_shutdown (); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 3\n"); + /* name in different zone */ + s_name_3 = "dummy3"; + s_rd_3 = create_record (1); + GNUNET_assert (NULL != (ns_ops[2] = + GNUNET_NAMESTORE_records_store (nsh, + &privkey2, + s_name_3, + 1, + s_rd_3, + &put_cont, + (void *) s_name_3))); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 1\n"); + s_name_1 = "dummy1"; + s_rd_1 = create_record (1); + GNUNET_assert (NULL != (ns_ops[0] = + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_1, + 1, + s_rd_1, + &put_cont, + (void *) s_name_1))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 2 \n"); + s_name_2 = "dummy2"; + s_rd_2 = create_record (1); + GNUNET_assert (NULL != (ns_ops[1] = + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_2, + 1, + s_rd_2, + &put_cont, + (void *) s_name_2))); +} + + +#include "test_common.c" + + +int +main (int argc, + char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-monitoring-existing", + cfg_name, + &run, + NULL)) + { + GNUNET_break (0); + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_monitoring_existing.c */ diff --git a/src/plugin/namestore/test_namestore_api_postgres.conf b/src/plugin/namestore/test_namestore_api_postgres.conf new file mode 100644 index 000000000..007168280 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_postgres.conf @@ -0,0 +1,10 @@ +@INLINE@ test_namestore_api.conf + +[namestore] +DATABASE = postgres + + +[namestore-postgres] +CONFIG = connect_timeout=10 dbname=gnunetcheck +TEMPORARY_TABLE = NO +INIT_ON_CONNECT = YES diff --git a/src/plugin/namestore/test_namestore_api_remove.c b/src/plugin/namestore/test_namestore_api_remove.c new file mode 100644 index 000000000..1a4a7c867 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_remove.c @@ -0,0 +1,219 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api.c + * @brief testcase for namestore_api.c to: remove record + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static int res; + +static int removed; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + + +static void +cleanup () +{ + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + cleanup (); + res = 1; +} + + +static void +end (void *cls) +{ + cleanup (); + res = 0; +} + + +static void +remove_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + nsqe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Records could not be removed: `%s'\n"), + GNUNET_ErrorCode_get_hint (ec)); + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, + NULL); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Records were removed, perform lookup\n"); + removed = GNUNET_YES; + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + GNUNET_SCHEDULER_add_now (&end, NULL); +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + GNUNET_assert (NULL != cls); + nsqe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore could not store record: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 0, NULL, + &remove_cont, (void *) name); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_GNSRECORD_Data rd; + const char *name = "dummy"; + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_key_get_public (&privkey, + &pubkey); + + removed = GNUNET_NO; + + rd.expiration_time = GNUNET_TIME_UNIT_MINUTES.rel_value_us; + rd.record_type = TEST_RECORD_TYPE; + rd.data_size = TEST_RECORD_DATALEN; + rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); + rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + memset ((char *) rd.data, + 'a', + TEST_RECORD_DATALEN); + + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &put_cont, + (void *) name); + if (NULL == nsqe) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore cannot store no block\n")); + } + GNUNET_free_nz ((void *) rd.data); +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-remove", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_remove.c */ diff --git a/src/plugin/namestore/test_namestore_api_remove_not_existing_record.c b/src/plugin/namestore/test_namestore_api_remove_not_existing_record.c new file mode 100644 index 000000000..11a69bea1 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_remove_not_existing_record.c @@ -0,0 +1,179 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_remove_not_existing_record.c + * @brief testcase for namestore_api.c + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE 1234 + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static int res; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + + +static void +cleanup (void) +{ + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + cleanup (); + res = 1; +} + + +static void +end (void *cls) +{ + cleanup (); + res = 0; +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + GNUNET_assert (NULL != cls); + nsqe = NULL; + if (endbadly_task != NULL) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + } + switch (ec) + { + case GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND: + /* We expect that the record is not found */ + GNUNET_SCHEDULER_add_now (&end, NULL); + break; + + case GNUNET_EC_NONE: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore could remove non-existing record: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + GNUNET_SCHEDULER_add_now (&endbadly, NULL); + break; + + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore failed: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + GNUNET_SCHEDULER_add_now (&endbadly, NULL); + break; + } +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + const char *name = "dummy"; + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_key_get_public (&privkey, &pubkey); + + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 0, NULL, + &put_cont, (void *) name); + if (NULL == nsqe) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore cannot store no block\n")); + } +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-remove-non-existing-record", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_remove_not_existing_record.c */ diff --git a/src/plugin/namestore/test_namestore_api_sqlite.conf b/src/plugin/namestore/test_namestore_api_sqlite.conf new file mode 100644 index 000000000..342356247 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_sqlite.conf @@ -0,0 +1,9 @@ +@INLINE@ test_namestore_api.conf + +[namestore] +DATABASE = sqlite +# PREFIX = valgrind --leak-check=full --track-origins=yes --log-file=/tmp/v_log + +[namestore-sqlite] +FILENAME = $GNUNET_TEST_HOME/namestore/sqlite_test.db +INIT_ON_CONNECT = YES diff --git a/src/plugin/namestore/test_namestore_api_store.c b/src/plugin/namestore/test_namestore_api_store.c new file mode 100644 index 000000000..22b92fbe5 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_store.c @@ -0,0 +1,172 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_store.c + * @brief testcase for namestore_api.c: store a record + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static int res; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + + +static void +cleanup () +{ + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + cleanup (); + res = 1; +} + + +static void +end (void *cls) +{ + cleanup (); + res = 0; +} + + +static void +put_cont (void *cls, enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + nsqe = NULL; + GNUNET_assert (NULL != cls); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + GNUNET_SCHEDULER_add_now (&end, NULL); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_GNSRECORD_Data rd; + const char *name = "dummy.dummy.gnunet"; + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, NULL); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_key_get_public (&privkey, &pubkey); + + + rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us; + rd.record_type = TEST_RECORD_TYPE; + rd.data_size = TEST_RECORD_DATALEN; + rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); + rd.flags = 0; + memset ((char *) rd.data, 'a', TEST_RECORD_DATALEN); + + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &put_cont, + (void *) name); + if (NULL == nsqe) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore cannot store no block\n")); + } + GNUNET_free_nz ((void *) rd.data); +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_store.c */ diff --git a/src/plugin/namestore/test_namestore_api_store_update.c b/src/plugin/namestore/test_namestore_api_store_update.c new file mode 100644 index 000000000..86495e261 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_store_update.c @@ -0,0 +1,269 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012, 2013, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_store_update.c + * @brief testcase for namestore_api.c: store a record, update it and perform a lookup + * @author Matthias Wachs + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TEST_RECORD_DATALEN2 234 + +#define TEST_RECORD_DATA2 'b' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static int res; + +static int update_performed; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + +static const char *name = "dummy"; + + +/** + * Terminate test with error. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + GNUNET_break (0); + endbadly_task = NULL; + GNUNET_SCHEDULER_shutdown (); + res = 1; +} + + +static void +end (void *cls) +{ + if (NULL != endbadly_task) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + } + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec); + + +static void +lookup_success (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char* label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct GNUNET_GNSRECORD_Data rd_new; + + GNUNET_assert (1 == rd_count); + GNUNET_assert (NULL != rd); + nsqe = NULL; + if (GNUNET_NO == update_performed) + { + char rd_cmp_data[TEST_RECORD_DATALEN]; + + memset (rd_cmp_data, + TEST_RECORD_DATA, + TEST_RECORD_DATALEN); + GNUNET_assert (TEST_RECORD_TYPE == rd[0].record_type); + GNUNET_assert (TEST_RECORD_DATALEN == rd[0].data_size); + GNUNET_assert (0 == memcmp (&rd_cmp_data, + rd[0].data, + TEST_RECORD_DATALEN)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Block was decrypted successfully, updating record \n"); + + rd_new.flags = GNUNET_GNSRECORD_RF_NONE; + rd_new.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us + + 1000000000; + rd_new.record_type = TEST_RECORD_TYPE; + rd_new.data_size = TEST_RECORD_DATALEN2; + rd_new.data = GNUNET_malloc (TEST_RECORD_DATALEN2); + memset ((char *) rd_new.data, + TEST_RECORD_DATA2, + TEST_RECORD_DATALEN2); + + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd_new, + &put_cont, + (void *) name); + GNUNET_free (rd_new.data); + update_performed = GNUNET_YES; + } + else + { + char rd_cmp_data[TEST_RECORD_DATALEN2]; + + memset (rd_cmp_data, + TEST_RECORD_DATA2, + TEST_RECORD_DATALEN2); + GNUNET_assert (TEST_RECORD_TYPE == rd[0].record_type); + GNUNET_assert (TEST_RECORD_DATALEN2 == rd[0].data_size); + GNUNET_assert (0 == memcmp (&rd_cmp_data, + rd[0].data, + TEST_RECORD_DATALEN2)); + GNUNET_SCHEDULER_shutdown (); + res = 0; + } +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + struct GNUNET_HashCode derived_hash; + + nsqe = NULL; + GNUNET_assert (NULL != cls); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + /* Create derived hash */ + GNUNET_GNSRECORD_query_from_private_key (&privkey, + name, + &derived_hash); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Looking in namestore for `%s'\n", + GNUNET_h2s (&derived_hash)); + nsqe = GNUNET_NAMESTORE_records_lookup (nsh, + &privkey, + name, + &endbadly, + (void *) name, + & lookup_success, + (void *) name); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_GNSRECORD_Data rd; + + update_performed = GNUNET_NO; + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + memset (&privkey, 0, sizeof (privkey)); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_key_get_public (&privkey, &pubkey); + rd.flags = GNUNET_GNSRECORD_RF_NONE; + rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us + 1000000000; + rd.record_type = TEST_RECORD_TYPE; + rd.data_size = TEST_RECORD_DATALEN; + rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); + memset ((char *) rd.data, + TEST_RECORD_DATA, + TEST_RECORD_DATALEN); + + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &put_cont, + (void *) name); + if (NULL == nsqe) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore cannot store no block\n")); + } + GNUNET_free_nz ((void *) rd.data); +} + + +#include "test_common.c" + + +int +main (int argc, + char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test--store-update", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_store_update.c */ diff --git a/src/plugin/namestore/test_namestore_api_tx_rollback.c b/src/plugin/namestore/test_namestore_api_tx_rollback.c new file mode 100644 index 000000000..4a701f60e --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_tx_rollback.c @@ -0,0 +1,264 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_tx_rollback.c + * @brief testcase for namestore_api_tx_rollback.c to: rollback changes in TX + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static int res; + +static int removed; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + + +static void +cleanup () +{ + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + if (NULL != nsqe) + { + GNUNET_NAMESTORE_cancel (nsqe); + nsqe = NULL; + } + cleanup (); + res = 1; +} + + +static void +end (void *cls) +{ + cleanup (); + res = 0; +} + +static void +lookup_it (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + GNUNET_assert (0 == rd_count); + GNUNET_SCHEDULER_add_now (&end, NULL); +} + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); +} + +static void +remove_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + nsqe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to roll back: `%s'\n"), + GNUNET_ErrorCode_get_hint (ec)); + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, + NULL); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rolled back, perform lookup\n"); + removed = GNUNET_YES; + if (NULL != endbadly_task) + GNUNET_SCHEDULER_cancel (endbadly_task); + /* FIXME not actually doing lookup here */ + nsqe = GNUNET_NAMESTORE_records_lookup (nsh, + &privkey, + (char*) cls, + &fail_cb, + NULL, + &lookup_it, + NULL); +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + const char *name = cls; + + GNUNET_assert (NULL != cls); + nsqe = NULL; + if (GNUNET_EC_NONE != ec) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Namestore could not store record: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + if (endbadly_task != NULL) + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = GNUNET_SCHEDULER_add_now (&endbadly, NULL); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh, remove_cont, + (void *) name); +} + +static void +begin_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + struct GNUNET_GNSRECORD_Data rd; + const char *name = cls; + + GNUNET_assert (GNUNET_EC_NONE == ec); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_key_get_public (&privkey, + &pubkey); + + removed = GNUNET_NO; + + rd.expiration_time = GNUNET_TIME_absolute_get ().abs_value_us; + rd.record_type = TEST_RECORD_TYPE; + rd.data_size = TEST_RECORD_DATALEN; + rd.data = GNUNET_malloc (TEST_RECORD_DATALEN); + rd.flags = 0; + memset ((char *) rd.data, + 'a', + TEST_RECORD_DATALEN); + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &put_cont, + (void *) name); + GNUNET_assert (NULL != nsqe); + GNUNET_free_nz ((void *) rd.data); +} + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + const char *name = "dummy"; + + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + nsqe = GNUNET_NAMESTORE_transaction_begin (nsh, begin_cont, (void *) name); + /*nsqe = GNUNET_NAMESTORE_transaction_commit (nsh, commit_cont); + nsqe = GNUNET_NAMESTORE_transaction_rollback (nsh, rollback_cont); Must also happen on disconnect + nsqe = GNUNET_NAMESTORE_records_edit (nsh, + &privkey, + name, + 1, + &rd, + &edit_cont, + (void *) name); + nsqe = GNUNET_NAMESTORE_records_insert_bulk (nsh, + count, + &rd, + & + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + name, + 1, + &rd, + &put_cont, + (void *) name);*/ + GNUNET_assert (NULL != nsqe); +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-remove", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_remove.c */ diff --git a/src/plugin/namestore/test_namestore_api_zone_iteration.c b/src/plugin/namestore/test_namestore_api_zone_iteration.c new file mode 100644 index 000000000..fb69fffcc --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_zone_iteration.c @@ -0,0 +1,464 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_zone_iteration.c + * @brief testcase for zone iteration functionality: iterate all zones + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PrivateKey privkey2; + +static struct GNUNET_NAMESTORE_ZoneIterator *zi; + +static int res; + +static int returned_records; + +static char *s_name_1; + +static struct GNUNET_GNSRECORD_Data *s_rd_1; + +static char *s_name_2; + +static struct GNUNET_GNSRECORD_Data *s_rd_2; + +static char *s_name_3; + +static struct GNUNET_GNSRECORD_Data *s_rd_3; + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + * @param tc scheduler context + */ +static void +endbadly (void *cls) +{ + endbadly_task = NULL; + GNUNET_SCHEDULER_shutdown (); + res = 1; +} + + +static void +end (void *cls) +{ + if (NULL != zi) + { + GNUNET_NAMESTORE_zone_iteration_stop (zi); + zi = NULL; + } + if (NULL != endbadly_task) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + } + GNUNET_free (s_name_1); + GNUNET_free (s_name_2); + GNUNET_free (s_name_3); + if (NULL != s_rd_1) + { + GNUNET_free_nz ((void *) s_rd_1->data); + GNUNET_free (s_rd_1); + } + if (NULL != s_rd_2) + { + GNUNET_free_nz ((void *) s_rd_2->data); + GNUNET_free (s_rd_2); + } + if (NULL != s_rd_3) + { + GNUNET_free_nz ((void *) s_rd_3->data); + GNUNET_free (s_rd_3); + } + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } +} + + +static void +zone_end (void *cls) +{ + GNUNET_break (3 == returned_records); + if (3 == returned_records) + { + res = 0; /* Last iteraterator callback, we are done */ + zi = NULL; + } + else + res = 1; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received last result, iteration done after receing %u results\n", + returned_records); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); +} + + +static void +zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + int failed = GNUNET_NO; + + GNUNET_assert (NULL != zone); + if (0 == GNUNET_memcmp (zone, + &privkey)) + { + if (0 == strcmp (label, s_name_1)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else if (0 == strcmp (label, s_name_2)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid record count\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Comparing result failed: got name `%s' for first zone\n", + label); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else if (0 == GNUNET_memcmp (zone, + &privkey2)) + { + if (0 == strcmp (label, s_name_3)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_3)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid record count\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Comparing result failed: got name `%s' for first zone\n", + label); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid zone\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + + if (failed == GNUNET_NO) + { + returned_records++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling namestore to send the next result\n"); + GNUNET_NAMESTORE_zone_iterator_next (zi, + 1); + } + else + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + res = 1; + } +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + static int c = 0; + + if (GNUNET_EC_NONE == ec) + { + c++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record %u \n", + c); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to created records: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + res = 1; + return; + } + + if (c == 3) + { + res = 1; + returned_records = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All records created, starting iteration over all zones \n"); + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &zone_proc, + NULL, + &zone_end, + NULL); + if (zi == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone iterator\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + res = 1; + return; + } + } +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_new_array (count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int c = 0; c < count; c++) + { + rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd[c].record_type = TEST_RECORD_TYPE; + rd[c].data_size = 50; + rd[c].data = GNUNET_malloc (50); + rd[c].flags = 0; + memset ((char *) rd[c].data, 'a', 50); + } + return rd; +} + + +/** + * Callback called from the zone iterator when we iterate over + * the empty zone. Check that we got no records and then + * start the actual tests by filling the zone. + */ +static void +empty_zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + GNUNET_assert (nsh == cls); + if (NULL != zone) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected empty zone but received zone private key\n")); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + res = 1; + return; + } + if ((NULL != label) || (NULL != rd) || (0 != rd_count)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected no zone content but received data\n")); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + res = 1; + return; + } + GNUNET_assert (0); +} + + +static void +empty_zone_end (void *cls) +{ + zi = NULL; + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record 1\n"); + + GNUNET_asprintf (&s_name_1, "dummy1"); + s_rd_1 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_1, + 1, s_rd_1, + &put_cont, + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 2 \n"); + GNUNET_asprintf (&s_name_2, "dummy2"); + s_rd_2 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_2, + 1, s_rd_2, + &put_cont, + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 3\n"); + /* name in different zone */ + GNUNET_asprintf (&s_name_3, "dummy3"); + s_rd_3 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey2, + s_name_3, + 1, + s_rd_3, + &put_cont, + NULL); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + /* first, iterate over empty namestore */ + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &empty_zone_proc, + nsh, + &empty_zone_end, + NULL); + if (NULL == zi) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone iterator\n"); + GNUNET_break (0); + res = 1; + GNUNET_SCHEDULER_shutdown (); + } +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-zone-iteration", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_zone_iteration.c */ diff --git a/src/plugin/namestore/test_namestore_api_zone_iteration_nick.c b/src/plugin/namestore/test_namestore_api_zone_iteration_nick.c new file mode 100644 index 000000000..c494051d0 --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_zone_iteration_nick.c @@ -0,0 +1,460 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_zone_iteration.c + * @brief testcase for zone iteration functionality: iterate all zones + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_gns_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define ZONE_NICK_1 "nick1" +#define ZONE_NICK_2 "nick2" + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PrivateKey privkey2; + +static struct GNUNET_NAMESTORE_ZoneIterator *zi; + +static int res; + +static int returned_records; + +static char *s_name_1; + +static struct GNUNET_GNSRECORD_Data *s_rd_1; + +static char *s_name_2; + +static struct GNUNET_GNSRECORD_Data *s_rd_2; + +static char *s_name_3; + +static struct GNUNET_GNSRECORD_Data *s_rd_3; + +static struct GNUNET_NAMESTORE_QueueEntry *nsqe; + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + * @param tc scheduler context + */ +static void +end (void *cls) +{ + if (NULL != zi) + { + GNUNET_NAMESTORE_zone_iteration_stop (zi); + zi = NULL; + } + if (nsh != NULL) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_free (s_name_1); + GNUNET_free (s_name_2); + GNUNET_free (s_name_3); + + if (s_rd_1 != NULL) + { + GNUNET_free_nz ((void *) s_rd_1->data); + GNUNET_free (s_rd_1); + } + if (s_rd_2 != NULL) + { + GNUNET_free_nz ((void *) s_rd_2->data); + GNUNET_free (s_rd_2); + } + if (s_rd_3 != NULL) + { + GNUNET_free_nz ((void *) s_rd_3->data); + GNUNET_free (s_rd_3); + } +} + + +static int +check_zone_1 (const char *label, unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + for (unsigned int c = 0; c < rd_count; c++) + { + if ((rd[c].record_type == GNUNET_GNSRECORD_TYPE_NICK) && + (0 != strcmp (rd[c].data, ZONE_NICK_1))) + { + GNUNET_break (0); + return GNUNET_YES; + } + } + return GNUNET_NO; +} + + +static int +check_zone_2 (const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + for (unsigned int c = 0; c < rd_count; c++) + { + if ((rd[c].record_type == GNUNET_GNSRECORD_TYPE_NICK) && + (0 != strcmp (rd[c].data, ZONE_NICK_2))) + { + GNUNET_break (0); + return GNUNET_YES; + } + } + return GNUNET_NO; +} + + +static void +zone_proc_end (void *cls) +{ + zi = NULL; + res = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received last result, iteration done after receing %u results\n", + returned_records); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + int failed = GNUNET_NO; + + GNUNET_assert (NULL != zone); + if (0 == GNUNET_memcmp (zone, &privkey)) + { + failed = check_zone_1 (label, rd_count, rd); + if (GNUNET_YES == failed) + GNUNET_break (0); + } + else if (0 == GNUNET_memcmp (zone, &privkey2)) + { + failed = check_zone_2 (label, rd_count, rd); + if (GNUNET_YES == failed) + GNUNET_break (0); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid zone\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + + if (failed == GNUNET_NO) + { + returned_records++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling namestore to send the next result\n"); + GNUNET_NAMESTORE_zone_iterator_next (zi, + 1); + } + else + { + GNUNET_break (0); + res = 1; + GNUNET_SCHEDULER_shutdown (); + } +} + + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + static int c = 0; + + if (GNUNET_EC_NONE == ec) + { + c++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record %u \n", c); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to created records: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (c == 3) + { + res = 1; + returned_records = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All records created, starting iteration over all zones \n"); + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &zone_proc, + NULL, + &zone_proc_end, + NULL); + if (zi == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create zone iterator\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + } +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_new_array (count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int c = 0; c < count; c++) + { + rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd[c].record_type = TEST_RECORD_TYPE; + rd[c].data_size = 50; + rd[c].data = GNUNET_malloc (50); + rd[c].flags = 0; + memset ((char *) rd[c].data, 'a', 50); + } + return rd; +} + + +static void +nick_2_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Nick added : %s\n", + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record 1\n"); + + GNUNET_asprintf (&s_name_1, "dummy1"); + s_rd_1 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, &privkey, s_name_1, + 1, s_rd_1, + &put_cont, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 2 \n"); + GNUNET_asprintf (&s_name_2, "dummy2"); + s_rd_2 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, &privkey, s_name_2, + 1, s_rd_2, &put_cont, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 3\n"); + + /* name in different zone */ + GNUNET_asprintf (&s_name_3, "dummy3"); + s_rd_3 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, &privkey2, s_name_3, + 1, s_rd_3, + &put_cont, NULL); +} + + +static void +nick_1_cont (void *cls, enum GNUNET_ErrorCode ec) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Nick 1 added : %s\n", + (GNUNET_EC_NONE == ec) ? "SUCCESS" : "FAIL"); + struct GNUNET_GNSRECORD_Data rd; + + memset (&rd, 0, sizeof(rd)); + rd.data = ZONE_NICK_2; + rd.data_size = strlen (ZONE_NICK_2) + 1; + rd.record_type = GNUNET_GNSRECORD_TYPE_NICK; + rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; + rd.flags |= GNUNET_GNSRECORD_RF_PRIVATE; + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey2, + GNUNET_GNS_EMPTY_LABEL_AT, + 1, + &rd, + &nick_2_cont, + &privkey2); + + if (NULL == nsqe) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore cannot store no block\n")); + } +} + + +/** + * Callback called from the zone iterator when we iterate over + * the empty zone. Check that we got no records and then + * start the actual tests by filling the zone. + */ +static void +empty_zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + GNUNET_assert (nsh == cls); + + if (NULL != zone) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected empty zone but received zone private key\n")); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + if ((NULL != label) || (NULL != rd) || (0 != rd_count)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected no zone content but received data\n")); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_assert (0); +} + + +static void +empty_zone_end (void *cls) +{ + GNUNET_assert (nsh == cls); + struct GNUNET_GNSRECORD_Data rd; + + zi = NULL; + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); + + memset (&rd, 0, sizeof(rd)); + rd.data = ZONE_NICK_1; + rd.data_size = strlen (ZONE_NICK_1) + 1; + rd.record_type = GNUNET_GNSRECORD_TYPE_NICK; + rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; + rd.flags |= GNUNET_GNSRECORD_RF_PRIVATE; + nsqe = GNUNET_NAMESTORE_records_store (nsh, + &privkey, + GNUNET_GNS_EMPTY_LABEL_AT, + 1, + &rd, + &nick_1_cont, + NULL); + if (NULL == nsqe) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Namestore cannot store no block\n")); + } +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + /* first, iterate over empty namestore */ + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &empty_zone_proc, + nsh, + &empty_zone_end, + nsh); + if (NULL == zi) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone iterator\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + } +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-zone-iteration-nick", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_zone_iteration.c */ diff --git a/src/plugin/namestore/test_namestore_api_zone_iteration_specific_zone.c b/src/plugin/namestore/test_namestore_api_zone_iteration_specific_zone.c new file mode 100644 index 000000000..02587706c --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_zone_iteration_specific_zone.c @@ -0,0 +1,447 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_zone_iteration_specific_zone.c + * @brief testcase for zone iteration functionality: iterate over a specific zone + * @author Matthias Wachs + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PrivateKey privkey2; + +static struct GNUNET_NAMESTORE_ZoneIterator *zi; + +static int res; + +static int returned_records; + +static char *s_name_1; + +static struct GNUNET_GNSRECORD_Data *s_rd_1; + +static char *s_name_2; + +static struct GNUNET_GNSRECORD_Data *s_rd_2; + +static char *s_name_3; + +static struct GNUNET_GNSRECORD_Data *s_rd_3; + + +/** + * Handle timeout. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + endbadly_task = NULL; + GNUNET_SCHEDULER_shutdown (); + res = 1; +} + + +static void +end (void *cls) +{ + if (NULL != zi) + { + GNUNET_NAMESTORE_zone_iteration_stop (zi); + zi = NULL; + } + if (NULL != endbadly_task) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + } + GNUNET_free (s_name_1); + GNUNET_free (s_name_2); + GNUNET_free (s_name_3); + if (s_rd_1 != NULL) + { + GNUNET_free_nz ((void *) s_rd_1->data); + GNUNET_free (s_rd_1); + } + if (s_rd_2 != NULL) + { + GNUNET_free_nz ((void *) s_rd_2->data); + GNUNET_free (s_rd_2); + } + if (s_rd_3 != NULL) + { + GNUNET_free_nz ((void *) s_rd_3->data); + GNUNET_free (s_rd_3); + } + if (nsh != NULL) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } +} + + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); + zi = NULL; +} + + +static void +zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + int failed = GNUNET_NO; + + GNUNET_assert (NULL != zone); + if (0 == GNUNET_memcmp (zone, + &privkey)) + { + if (0 == strcmp (label, s_name_1)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else if (0 == strcmp (label, s_name_2)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid record count\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Comparing result failed: got name `%s' for first zone\n", + label); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else if (0 == GNUNET_memcmp (zone, &privkey2)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received data for not requested zone\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid zone\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + if (failed == GNUNET_NO) + { + returned_records++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling namestore to send the next result\n"); + GNUNET_NAMESTORE_zone_iterator_next (zi, + 1); + } + else + { + GNUNET_break (0); + res = 2; + GNUNET_SCHEDULER_shutdown (); + } +} + + +static void +zone_proc_end (void *cls) +{ + zi = NULL; + GNUNET_break (2 == returned_records); + if (2 == returned_records) + { + res = 0; /* Last iteraterator callback, we are done */ + } + else + { + res = 1; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received last result, iteration done after receing %u results\n", + returned_records); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + static int c = 0; + + if (GNUNET_EC_NONE == ec) + { + c++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record %u \n", c); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to created records: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + GNUNET_break (0); + res = 2; + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (c == 3) + { + res = 1; + returned_records = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All records created, starting iteration over all zones \n"); + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + &privkey, + &fail_cb, + NULL, + &zone_proc, + NULL, + &zone_proc_end, + NULL); + if (zi == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone iterator\n"); + GNUNET_break (0); + res = 2; + GNUNET_SCHEDULER_shutdown (); + return; + } + } +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_new_array (count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int c = 0; c < count; c++) + { + rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd[c].record_type = TEST_RECORD_TYPE; + rd[c].data_size = 50; + rd[c].data = GNUNET_malloc (50); + rd[c].flags = 0; + memset ((char *) rd[c].data, 'a', 50); + } + return rd; +} + + +/** + * Callback called from the zone iterator when we iterate over + * the empty zone. Check that we got no records and then + * start the actual tests by filling the zone. + */ +static void +empty_zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + GNUNET_assert (nsh == cls); + if (NULL != zone) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected empty zone but received zone private key\n")); + GNUNET_break (0); + res = 2; + GNUNET_SCHEDULER_shutdown (); + return; + } + if ((NULL != label) || (NULL != rd) || (0 != rd_count)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected no zone content but received data\n")); + GNUNET_break (0); + res = 2; + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_assert (0); +} + + +static void +empty_zone_proc_end (void *cls) +{ + zi = NULL; + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 1\n"); + GNUNET_asprintf (&s_name_1, + "dummy1"); + s_rd_1 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_1, + 1, + s_rd_1, + &put_cont, + NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 2 \n"); + GNUNET_asprintf (&s_name_2, + "dummy2"); + s_rd_2 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_2, + 1, + s_rd_2, + &put_cont, + NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 3\n"); + + /* name in different zone */ + GNUNET_asprintf (&s_name_3, + "dummy3"); + s_rd_3 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey2, + s_name_3, + 1, s_rd_3, + &put_cont, + NULL); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + /* first, iterate over empty namestore */ + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &empty_zone_proc, + nsh, + &empty_zone_proc_end, + nsh); + if (NULL == zi) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone iterator\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + } +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ( + "test-namestore-api-zone-iteration-specific-zone", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_zone_iteration_specific_zone.c */ diff --git a/src/plugin/namestore/test_namestore_api_zone_iteration_stop.c b/src/plugin/namestore/test_namestore_api_zone_iteration_stop.c new file mode 100644 index 000000000..b6b0787ef --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_zone_iteration_stop.c @@ -0,0 +1,449 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_zone_iteration_stop.c + * @brief testcase for zone iteration functionality: stop iterating of zones + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) +#define WAIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2) + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PrivateKey privkey2; + +static struct GNUNET_NAMESTORE_ZoneIterator *zi; + +static int res; + +static int returned_records; + +static char *s_name_1; + +static struct GNUNET_GNSRECORD_Data *s_rd_1; + +static char *s_name_2; + +static struct GNUNET_GNSRECORD_Data *s_rd_2; + +static char *s_name_3; + +static struct GNUNET_GNSRECORD_Data *s_rd_3; + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +end (void *cls) +{ + if (NULL != zi) + { + GNUNET_NAMESTORE_zone_iteration_stop (zi); + zi = NULL; + } + if (nsh != NULL) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } + GNUNET_free (s_name_1); + GNUNET_free (s_name_2); + GNUNET_free (s_name_3); + if (s_rd_1 != NULL) + { + GNUNET_free_nz ((void *) s_rd_1->data); + GNUNET_free (s_rd_1); + } + if (s_rd_2 != NULL) + { + GNUNET_free_nz ((void *) s_rd_2->data); + GNUNET_free (s_rd_2); + } + if (s_rd_3 != NULL) + { + GNUNET_free_nz ((void *) s_rd_3->data); + GNUNET_free (s_rd_3); + } +} + + +static void +delayed_end (void *cls) +{ + GNUNET_SCHEDULER_shutdown (); +} + + +static void +fail_cb (void *cls) +{ + GNUNET_assert (0); +} + + +static void +zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + int failed = GNUNET_NO; + + GNUNET_assert (NULL != zone); + if (0 == GNUNET_memcmp (zone, &privkey)) + { + if (0 == strcmp (label, s_name_1)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_1)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else if (0 == strcmp (label, s_name_2)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_2)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid record count\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Comparing result failed: got name `%s' for first zone\n", + label); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else if (0 == GNUNET_memcmp (zone, &privkey2)) + { + if (0 == strcmp (label, s_name_3)) + { + if (rd_count == 1) + { + if (GNUNET_YES != GNUNET_GNSRECORD_records_cmp (rd, s_rd_3)) + { + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid record count\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Comparing result failed: got name `%s' for first zone\n", + label); + failed = GNUNET_YES; + GNUNET_break (0); + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received invalid zone\n"); + failed = GNUNET_YES; + GNUNET_break (0); + } + if (failed == GNUNET_NO) + { + if (1 == returned_records) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling namestore to stop zone iteration\n"); + GNUNET_NAMESTORE_zone_iteration_stop (zi); + zi = NULL; + res = 0; + GNUNET_SCHEDULER_add_delayed (WAIT, + &delayed_end, + NULL); + return; + } + returned_records++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling namestore to send the next result\n"); + GNUNET_NAMESTORE_zone_iterator_next (zi, + 1); + } + else + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + } +} + + +static void +zone_proc_end (void *cls) +{ + GNUNET_break (1 <= returned_records); + if (1 >= returned_records) + res = 1; /* Last iteraterator callback, we are done */ + else + res = 0; + zi = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received last result, iteration done after receing %u results\n", + returned_records); + GNUNET_SCHEDULER_add_now (&end, NULL); +} + + +static void +put_cont (void *cls, enum GNUNET_ErrorCode ec) +{ + static int c = 0; + + if (GNUNET_EC_NONE == ec) + { + c++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Created record %u \n", c); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to created records: `%s'\n", + GNUNET_ErrorCode_get_hint (ec)); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (c == 3) + { + res = 1; + returned_records = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "All records created, starting iteration over all zones \n"); + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &zone_proc, + NULL, + &zone_proc_end, + NULL); + if (zi == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone iterator\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + } +} + + +static struct GNUNET_GNSRECORD_Data * +create_record (unsigned int count) +{ + struct GNUNET_GNSRECORD_Data *rd; + + rd = GNUNET_new_array (count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int c = 0; c < count; c++) + { + rd[c].expiration_time = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_HOURS).abs_value_us; + rd[c].record_type = TEST_RECORD_TYPE; + rd[c].data_size = 50; + rd[c].data = GNUNET_malloc (50); + rd[c].flags = 0; + memset ((char *) rd[c].data, 'a', 50); + } + return rd; +} + + +/** + * Callback called from the zone iterator when we iterate over + * the empty zone. Check that we got no records and then + * start the actual tests by filling the zone. + */ +static void +empty_zone_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + GNUNET_assert (nsh == cls); + if (NULL != zone) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected empty zone but received zone private key\n")); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + if ((NULL != label) || (NULL != rd) || (0 != rd_count)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Expected no zone content but received data\n")); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_assert (0); +} + + +static void +empty_zone_proc_end (void *cls) +{ + GNUNET_assert (nsh == cls); + zi = NULL; + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + privkey2.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + GNUNET_CRYPTO_ecdsa_key_create (&privkey2.ecdsa_key); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 1\n"); + + GNUNET_asprintf (&s_name_1, + "dummy1"); + s_rd_1 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey, s_name_1, + 1, s_rd_1, &put_cont, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 2 \n"); + GNUNET_asprintf (&s_name_2, + "dummy2"); + s_rd_2 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name_2, + 1, + s_rd_2, + &put_cont, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created record 3\n"); + + /* name in different zone */ + GNUNET_asprintf (&s_name_3, "dummy3"); + s_rd_3 = create_record (1); + GNUNET_NAMESTORE_records_store (nsh, + &privkey2, + s_name_3, + 1, + s_rd_3, + &put_cont, NULL); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + /* first, iterate over empty namestore */ + zi = GNUNET_NAMESTORE_zone_iteration_start (nsh, + NULL, + &fail_cb, + NULL, + &empty_zone_proc, + nsh, + &empty_zone_proc_end, + nsh); + if (NULL == zi) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create zone iterator\n"); + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + } +} + + +#include "test_common.c" + + +int +main (int argc, char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-zone-iteration-stop", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + + return res; +} + + +/* end of test_namestore_api_zone_iteration_stop.c */ diff --git a/src/plugin/namestore/test_namestore_api_zone_to_name.c b/src/plugin/namestore/test_namestore_api_zone_to_name.c new file mode 100644 index 000000000..c70eef53a --- /dev/null +++ b/src/plugin/namestore/test_namestore_api_zone_to_name.c @@ -0,0 +1,266 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/test_namestore_api_zone_to_name.c + * @brief testcase for zone to name translation + */ +#include "platform.h" +#include "gnunet_namestore_service.h" +#include "gnunet_testing_lib.h" +#include "namestore.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +#define RECORDS 5 + +#define TEST_RECORD_DATALEN 123 + +#define TEST_RECORD_DATA 'a' + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 100) + + +static struct GNUNET_NAMESTORE_Handle *nsh; + +static struct GNUNET_SCHEDULER_Task *endbadly_task; + +static struct GNUNET_CRYPTO_PrivateKey privkey; + +static struct GNUNET_CRYPTO_PublicKey pubkey; + +static struct GNUNET_CRYPTO_PublicKey s_zone_value; + +static char *s_name; + +static int res; + +static struct GNUNET_NAMESTORE_QueueEntry *qe; + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +endbadly (void *cls) +{ + (void) cls; + GNUNET_SCHEDULER_shutdown (); + res = 1; +} + + +static void +end (void *cls) +{ + if (NULL != qe) + { + GNUNET_NAMESTORE_cancel (qe); + qe = NULL; + } + if (NULL != endbadly_task) + { + GNUNET_SCHEDULER_cancel (endbadly_task); + endbadly_task = NULL; + } + if (NULL != nsh) + { + GNUNET_NAMESTORE_disconnect (nsh); + nsh = NULL; + } +} + + +static void +zone_to_name_proc (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *n, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + int fail = GNUNET_NO; + + qe = NULL; + if ((NULL == zone_key) && + (NULL == n) && + (0 == rd_count) && + (NULL == rd)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No result found\n"); + res = 1; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Result found: `%s'\n", + n); + if ((NULL == n) || + (0 != strcmp (n, + s_name))) + { + fail = GNUNET_YES; + GNUNET_break (0); + } + if (1 != rd_count) + { + fail = GNUNET_YES; + GNUNET_break (0); + } + if ((NULL == zone_key) || + (0 != GNUNET_memcmp (zone_key, + &privkey))) + { + fail = GNUNET_YES; + GNUNET_break (0); + } + if (fail == GNUNET_NO) + res = 0; + else + res = 1; + } + GNUNET_SCHEDULER_add_now (&end, + NULL); +} + + +static void +error_cb (void *cls) +{ + (void) cls; + qe = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Not found!\n"); + GNUNET_SCHEDULER_shutdown (); + res = 2; +} + + +static void +put_cont (void *cls, + enum GNUNET_ErrorCode ec) +{ + char *name = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name store added record for `%s': %s\n", + name, + (GNUNET_EC_NONE == ec) ? + "SUCCESS" : GNUNET_ErrorCode_get_hint (ec)); + if (GNUNET_EC_NONE == ec) + { + res = 0; + + qe = GNUNET_NAMESTORE_zone_to_name (nsh, + &privkey, + &s_zone_value, + &error_cb, + NULL, + &zone_to_name_proc, + NULL); + } + else + { + res = 1; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to put records for name `%s'\n", + name); + GNUNET_SCHEDULER_add_now (&end, + NULL); + } +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + (void) cls; + (void) peer; + endbadly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &endbadly, + NULL); + GNUNET_SCHEDULER_add_shutdown (&end, + NULL); + GNUNET_asprintf (&s_name, "dummy"); + privkey.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + GNUNET_CRYPTO_ecdsa_key_create (&privkey.ecdsa_key); + /* get public key */ + GNUNET_CRYPTO_key_get_public (&privkey, + &pubkey); + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &s_zone_value, + sizeof(s_zone_value)); + s_zone_value.type = htonl (GNUNET_GNSRECORD_TYPE_PKEY); + { + struct GNUNET_GNSRECORD_Data rd; + + rd.expiration_time = GNUNET_TIME_UNIT_HOURS.rel_value_us; + rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY; + rd.data_size = sizeof (s_zone_value.ecdsa_key); + rd.data = &s_zone_value.ecdsa_key; + rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + + nsh = GNUNET_NAMESTORE_connect (cfg); + GNUNET_break (NULL != nsh); + GNUNET_NAMESTORE_records_store (nsh, + &privkey, + s_name, + 1, + &rd, + &put_cont, + s_name); + } +} + + +#include "test_common.c" + + +int +main (int argc, + char *argv[]) +{ + const char *plugin_name; + char *cfg_name; + + (void) argc; + SETUP_CFG (plugin_name, cfg_name); + res = 1; + if (0 != + GNUNET_TESTING_peer_run ("test-namestore-api-zone-to-name", + cfg_name, + &run, + NULL)) + { + res = 1; + } + GNUNET_DISK_purge_cfg_dir (cfg_name, + "GNUNET_TEST_HOME"); + GNUNET_free (plugin_name); + GNUNET_free (cfg_name); + return res; +} + + +/* end of test_namestore_api_zone_to_name.c */ diff --git a/src/plugin/namestore/test_plugin_namestore.c b/src/plugin/namestore/test_plugin_namestore.c new file mode 100644 index 000000000..388b23f57 --- /dev/null +++ b/src/plugin/namestore/test_plugin_namestore.c @@ -0,0 +1,218 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/* + * @file namestore/test_plugin_namestore.c + * @brief Test for the namestore plugins + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_namestore_plugin.h" +#include "gnunet_testing_lib.h" + +#define TEST_RECORD_TYPE GNUNET_DNSPARSER_TYPE_TXT + +static int ok; + +/** + * Name of plugin under test. + */ +static const char *plugin_name; + + +/** + * Function called when the service shuts down. Unloads our namestore + * plugin. + * + * @param api api to unload + */ +static void +unload_plugin (struct GNUNET_NAMESTORE_PluginFunctions *api) +{ + char *libname; + + GNUNET_asprintf (&libname, "libgnunet_plugin_namestore_%s", plugin_name); + GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api)); + GNUNET_free (libname); +} + + +/** + * Load the namestore plugin. + * + * @param cfg configuration to pass + * @return NULL on error + */ +static struct GNUNET_NAMESTORE_PluginFunctions * +load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_NAMESTORE_PluginFunctions *ret; + char *libname; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loading `%s' namestore plugin\n", + plugin_name); + GNUNET_asprintf (&libname, + "libgnunet_plugin_namestore_%s", + plugin_name); + if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void *) cfg))) + { + fprintf (stderr, + "Failed to load plugin `%s'!\n", + plugin_name); + GNUNET_free (libname); + return NULL; + } + GNUNET_free (libname); + if (GNUNET_OK != ret->drop_tables (ret->cls)) + { + GNUNET_break (0); + return NULL; + } + if (GNUNET_OK != ret->create_tables (ret->cls)) + { + GNUNET_break (0); + return NULL; + } + return ret; +} + + +static void +test_record (void *cls, + uint64_t seq, + const struct GNUNET_CRYPTO_PrivateKey *private_key, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + int *idp = cls; + int id = *idp; + struct GNUNET_CRYPTO_PrivateKey tzone_private_key; + char tname[64]; + unsigned int trd_count = 1 + (id % 1024); + + GNUNET_snprintf (tname, sizeof(tname), "a%u", (unsigned int) id); + GNUNET_assert (trd_count == rd_count); + for (unsigned int i = 0; i < trd_count; i++) + { + GNUNET_assert (rd[i].data_size == id % 10); + GNUNET_assert (0 == memcmp ("Hello World", rd[i].data, id % 10)); + GNUNET_assert (rd[i].record_type == TEST_RECORD_TYPE); + GNUNET_assert (rd[i].flags == 0); + } + memset (&tzone_private_key, (id % 241), sizeof(tzone_private_key)); + GNUNET_assert (0 == strcmp (label, tname)); + GNUNET_assert (0 == GNUNET_memcmp (&tzone_private_key, private_key)); +} + + +static void +get_record (struct GNUNET_NAMESTORE_PluginFunctions *nsp, int id) +{ + GNUNET_assert ( + GNUNET_OK == + nsp->iterate_records (nsp->cls, NULL, 0, 1, &test_record, &id)); +} + + +static void +put_record (struct GNUNET_NAMESTORE_PluginFunctions *nsp, int id) +{ + struct GNUNET_CRYPTO_PrivateKey zone_private_key; + char label[64]; + unsigned int rd_count = 1 + (id % 1024); + struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; + struct GNUNET_CRYPTO_EcdsaSignature signature; + + GNUNET_snprintf (label, sizeof(label), "a%u", (unsigned int) id); + for (unsigned int i = 0; i < rd_count; i++) + { + rd[i].data = "Hello World"; + rd[i].data_size = id % 10; + rd[i].expiration_time = + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES).abs_value_us; + rd[i].record_type = TEST_RECORD_TYPE; + rd[i].flags = 0; + } + memset (&zone_private_key, (id % 241), sizeof(zone_private_key)); + memset (&signature, (id % 243), sizeof(signature)); + GNUNET_assert ( + GNUNET_OK == + nsp->store_records (nsp->cls, &zone_private_key, label, rd_count, rd)); +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_NAMESTORE_PluginFunctions *nsp; + + ok = 0; + nsp = load_plugin (cfg); + if (NULL == nsp) + { + fprintf ( + stderr, + "%s", + "Failed to initialize namestore. Database likely not setup, skipping test.\n"); + return; + } + put_record (nsp, 1); + get_record (nsp, 1); +#ifndef DARWIN // #5582 + unload_plugin (nsp); +#endif +} + + +int +main (int argc, char *argv[]) +{ + char cfg_name[PATH_MAX]; + char *const xargv[] = { "test-plugin-namestore", "-c", cfg_name, NULL }; + struct GNUNET_GETOPT_CommandLineOption options[] = + { GNUNET_GETOPT_OPTION_END }; + + GNUNET_log_setup ("test-plugin-namestore", "WARNING", NULL); + plugin_name = GNUNET_STRINGS_get_suffix_from_binary_name (argv[0]); + GNUNET_snprintf (cfg_name, + sizeof(cfg_name), + "test_plugin_namestore_%s.conf", + plugin_name); + GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TMP"); + GNUNET_PROGRAM_run ((sizeof(xargv) / sizeof(char *)) - 1, + xargv, + "test-plugin-namestore", + "nohelp", + options, + &run, + NULL); + GNUNET_DISK_purge_cfg_dir (cfg_name, "GNUNET_TMP"); + if (ok != 0) + fprintf (stderr, "Missed some testcases: %d\n", ok); + return ok; +} + + +/* end of test_plugin_namestore.c */ diff --git a/src/plugin/namestore/test_plugin_namestore_postgres.conf b/src/plugin/namestore/test_plugin_namestore_postgres.conf new file mode 100644 index 000000000..140d54623 --- /dev/null +++ b/src/plugin/namestore/test_plugin_namestore_postgres.conf @@ -0,0 +1,4 @@ +[namestore-postgres] +CONFIG = connect_timeout=10 dbname=gnunetcheck +INIT_ON_CONNECT = YES +#TEMPORARY_TABLE = YES diff --git a/src/plugin/namestore/test_plugin_namestore_sqlite.conf b/src/plugin/namestore/test_plugin_namestore_sqlite.conf new file mode 100644 index 000000000..365198db2 --- /dev/null +++ b/src/plugin/namestore/test_plugin_namestore_sqlite.conf @@ -0,0 +1,3 @@ +[namestore-sqlite] +FILENAME = $GNUNET_TMP/gnunet-test-plugin-namestore-sqlite/sqlite.db +INIT_ON_CONNECT = YES diff --git a/src/plugin/namestore/test_plugin_rest_namestore.sh b/src/plugin/namestore/test_plugin_rest_namestore.sh new file mode 100755 index 000000000..4f117db8b --- /dev/null +++ b/src/plugin/namestore/test_plugin_rest_namestore.sh @@ -0,0 +1,131 @@ +#!/bin/sh +trap "gnunet-arm -e -c test_gns_lookup.conf" SIGINT + +LOCATION=$(which gnunet-config) +if [ -z $LOCATION ] +then + LOCATION="gnunet-config" +fi +$LOCATION --version 1> /dev/null +if test $? != 0 +then + echo "GNUnet command line tools cannot be found, check environmental variables PATH and GNUNET_PREFIX" + exit 77 +fi + +rm -rf `gnunet-config -c test_namestore_api.conf -f -s paths -o GNUNET_TEST_HOME` + +namestore_link="http://localhost:7776/namestore" +wrong_link="http://localhost:7776/namestoreandmore" + +curl_get () { + #$1 is link + #$2 is grep + resp=$(curl -v "$1" 2>&1) + cache="$(echo $resp | grep "$2")" + #echo $cache + if [ "" = "$cache" ] + then + echo "Error in get response: $resp, expected $2" + gnunet-arm -e -c test_namestore_api.conf + exit 1 + fi +} + +curl_post () { + #$1 is link + #$2 is data + #$3 is grep + resp=$(curl -v -X "POST" "$1" --data "$2" 2>&1) + cache="$(echo $resp | grep "$3")" + #echo $cache + if [ "" = "$cache" ] + then + echo "Error in post response: $resp ($2), expected $3" + gnunet-arm -e -c test_namestore_api.conf + exit 1 + fi +} + +curl_delete () { + #$1 is link + #$2 is grep + resp=$(curl -v -X "DELETE" "$1" 2>&1) + cache="$(echo $resp | grep "$2")" + #echo $cache + if [ "" = "$cache" ] + then + echo "Error in delete response: $resp, expected $2" + gnunet-arm -e -c test_namestore_api.conf + exit 1 + fi +} + +# curl_put () { +# #$1 is link +# #$2 is data +# #$3 is grep +# cache="$(curl -v -X "PUT" "$1" --data "$2" 2>&1 | grep "$3")" +# #echo $cache +# if [ "" == "$cache" ] +# then +# exit 1 +# fi +# } + +#Test subsystem default identity + +TEST_ID="test" +gnunet-arm -s -c test_namestore_api.conf +#Test GET +gnunet-identity -C $TEST_ID -c test_namestore_api.conf +test="$(gnunet-namestore -D -z $TEST_ID -c test_namestore_api.conf)" +name=$TEST_ID +public="$(gnunet-identity -d -c test_namestore_api.conf | grep $TEST_ID | awk 'NR==1{print $3}')" +echo "$name $public" +gnunet-namestore -z $name -p -a -n "test_entry" -e "1d" -V "000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8" -t "PKEY" -c test_namestore_api.conf +sleep 1 +gnunet-arm -i rest -c test_namestore_api.conf +sleep 1 +curl_get "${namestore_link}/$name" "HTTP/1.1 200 OK" +curl_get "${namestore_link}/$public" "error" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf + +#Test POST with NAME +curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration": 86400000000, "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "HTTP/1.1 204 No Content" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + +# invalid values +curl_post "${namestore_link}/$name" '{"data": [{"value":"HVX38H2CB7WJM0WCPWT9CFX6GASMYJVR65RN75SJSSKAYVYXHMRGxxx", "record_type":"PKEY", "relative_expiration": 86400000000, "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "error" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + + +curl_post "${namestore_link}/$name" '{"data": [{"value":"", "record_type":"PKEY", "relative_expiration": 86400000000,"flag":0,"record_name"}]:"test_entry"}' "error" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + +curl_post "${namestore_link}/$name" '{"data": [{"record_type":"PKEY", "relative_expiration": 86400000000,"flag":0}],"record_name":"test_entry"}' "error" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + +#expirations +curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":0, "is_relative_expiration": true, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "HTTP/1.1 204" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + +curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":864000000000000, "is_relative_expiration": true, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "HTTP/1.1 204" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + +curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "expiration_time_missing":"1d", "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":"test_entry"}' "error" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + +#record_name +curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":86400000000, "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name":""}' "error" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 +curl_post "${namestore_link}/$name" '{"data": [{"value":"000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8", "record_type":"PKEY", "relative_expiration":"1d", "is_relative_expiration": false, "is_supplemental": false, "is_shadow": false, "is_private": false}],"record_name_missing":"test_entry"}' "error" +gnunet-namestore -z $name -d -n "test_entry" -c test_namestore_api.conf > /dev/null 2>&1 + +#Test DELETE +gnunet-namestore -z $name -p -a -n "test_entry" -e "1d" -V "000G006WVZ8HQ5YTVFNX09HK0VJVVQ9ZCBYDSCH3ERT04N5ZRBKEB82EP8" -t "PKEY" -c test_namestore_api.conf +curl_delete "${namestore_link}/$name/test_entry" "HTTP/1.1 204" + +gnunet-arm -e -c test_namestore_api.conf +exit 0; + diff --git a/src/pt/Makefile.am b/src/pt/Makefile.am index 567cbb6d7..5ecaaa10c 100644 --- a/src/pt/Makefile.am +++ b/src/pt/Makefile.am @@ -53,7 +53,7 @@ EXTRA_DIST = \ test_gns_vpn_SOURCES = \ test_gns_vpn.c test_gns_vpn_LDADD = $(MHD_LIBS) @LIBCURL@ \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/testing/libgnunettesting.la \ diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am index c052dd2a8..406aad081 100644 --- a/src/reclaim/Makefile.am +++ b/src/reclaim/Makefile.am @@ -63,7 +63,7 @@ libgnunet_plugin_rest_reclaim_la_LIBADD = \ $(top_builddir)/src/lib/json/libgnunetjson.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/rest/libgnunetrest.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ $(LTLIBINTL) -ljansson $(MHD_LIBS) libgnunet_plugin_rest_reclaim_la_LDFLAGS = \ @@ -79,7 +79,7 @@ libgnunet_plugin_rest_openid_connect_la_LIBADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ libgnunetreclaim.la \ $(top_builddir)/src/service/rest/libgnunetrest.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/lib/util/libgnunetutil.la $(XLIBS) \ @@ -123,7 +123,7 @@ gnunet_service_reclaim_LDADD = \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ libgnunetreclaim.la \ $(top_builddir)/src/gns/libgnunetgns.la \ @@ -154,7 +154,7 @@ libgnunetdid_la_LIBADD = \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ -ljansson \ $(GN_LIBINTL) $(XLIB) libgnunetdid_la_LDFLAGS = \ @@ -200,7 +200,7 @@ gnunet_reclaim_SOURCES = \ gnunet-reclaim.c gnunet_reclaim_LDADD = \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ libgnunetreclaim.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(GN_LIBINTL) @@ -219,7 +219,7 @@ gnunet_did_LDADD = \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ libgnunetdid.la \ -ljansson @@ -231,7 +231,7 @@ test_did_helper_LDADD = \ $(top_builddir)/src/gns/libgnunetgns.la \ $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ libgnunetdid.la \ -ljansson diff --git a/src/service/Makefile.am b/src/service/Makefile.am index 02148d0c7..636caa985 100644 --- a/src/service/Makefile.am +++ b/src/service/Makefile.am @@ -18,5 +18,6 @@ SUBDIRS = \ datastore \ dht \ namecache \ + namestore \ regex \ cadet diff --git a/src/service/namestore/.gitignore b/src/service/namestore/.gitignore new file mode 100644 index 000000000..7b99c6163 --- /dev/null +++ b/src/service/namestore/.gitignore @@ -0,0 +1,57 @@ +gnunet-service-namestore +gnunet-namestore-fcfsd +test_namestore_api_lookup_nick.nc +test_namestore_api_lookup_private.nc +test_namestore_api_lookup_public.nc +test_namestore_api_lookup_shadow.nc +test_namestore_api_lookup_shadow_filter.nc +test_namestore_api_monitoring.nc +test_namestore_api_monitoring_existing.nc +test_namestore_api_remove.nc +test_namestore_api_remove_not_existing_record.nc +test_namestore_api_store.nc +test_namestore_api_store_update.nc +test_namestore_api_zone_iteration.nc +test_namestore_api_zone_iteration_nick.nc +test_namestore_api_zone_iteration_specific_zone.nc +test_namestore_api_zone_iteration_stop.nc +test_plugin_namestore_postgres +test_plugin_namestore_sqlite +test_namestore_api_lookup_nick_postgres +test_namestore_api_lookup_nick_sqlite +test_namestore_api_lookup_private_postgres +test_namestore_api_lookup_private_sqlite +test_namestore_api_lookup_public_postgres +test_namestore_api_lookup_public_sqlite +test_namestore_api_lookup_shadow_filter_postgres +test_namestore_api_lookup_shadow_filter_sqlite +test_namestore_api_lookup_shadow_postgres +test_namestore_api_lookup_shadow_sqlite +test_namestore_api_monitoring_existing_postgres +test_namestore_api_monitoring_existing_sqlite +test_namestore_api_monitoring_postgres +test_namestore_api_monitoring_sqlite +test_namestore_api_remove_not_existing_record_postgres +test_namestore_api_remove_not_existing_record_sqlite +test_namestore_api_remove_postgres +test_namestore_api_remove_sqlite +test_namestore_api_store_postgres +test_namestore_api_store_sqlite +test_namestore_api_store_update_postgres +test_namestore_api_store_update_sqlite +test_namestore_api_zone_iteration_nick_postgres +test_namestore_api_zone_iteration_nick_sqlite +test_namestore_api_zone_iteration_postgres +test_namestore_api_zone_iteration_specific_zone_postgres +test_namestore_api_zone_iteration_specific_zone_sqlite +test_namestore_api_zone_iteration_sqlite +test_namestore_api_zone_iteration_stop_postgres +test_namestore_api_zone_iteration_stop_sqlite +test_namestore_api_zone_to_name_postgres +test_namestore_api_zone_to_name_sqlite +test_namestore_api_tx_rollback_postgres +test_namestore_api_tx_rollback_sqlite +test_namestore_api_edit_records_postgres +perf_namestore_api_import_sqlite +perf_namestore_api_import_postgres +test_namestore_api_store_locking_sqlite diff --git a/src/service/namestore/Makefile.am b/src/service/namestore/Makefile.am new file mode 100644 index 000000000..7f49e680f --- /dev/null +++ b/src/service/namestore/Makefile.am @@ -0,0 +1,63 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS) + +plugindir = $(libdir)/gnunet + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +pkgcfg_DATA = \ + namestore.conf + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIBS = -lgcov +endif + +lib_LTLIBRARIES = \ + libgnunetnamestore.la + +libexec_PROGRAMS = \ + gnunet-service-namestore + +libexec_PROGRAMS += \ + gnunet-namestore-fcfsd + + +libgnunetnamestore_la_SOURCES = \ + namestore_api.c \ + namestore_api_monitor.c \ + namestore.h +libgnunetnamestore_la_LIBADD = \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(GN_LIBINTL) +libgnunetnamestore_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) \ + -version-info 0:1:0 + +gnunet_namestore_fcfsd_SOURCES = \ + gnunet-namestore-fcfsd.c +gnunet_namestore_fcfsd_LDADD = $(MHD_LIBS) \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + libgnunetnamestore.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + $(top_builddir)/src/lib/json/libgnunetjson.la \ + $(GN_LIBINTL) -ljansson +gnunet_namestore_fcfsd_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS) + + +gnunet_service_namestore_SOURCES = \ + gnunet-service-namestore.c +gnunet_service_namestore_LDADD = \ + $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ + $(top_builddir)/src/lib/gnsrecord/libgnunetgnsrecord.la \ + $(top_builddir)/src/service/identity/libgnunetidentity.la \ + $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/lib/util/libgnunetutil.la \ + libgnunetnamestore.la \ + $(GN_LIBINTL) diff --git a/src/service/namestore/gnunet-namestore-fcfsd.c b/src/service/namestore/gnunet-namestore-fcfsd.c new file mode 100644 index 000000000..4948ae441 --- /dev/null +++ b/src/service/namestore/gnunet-namestore-fcfsd.c @@ -0,0 +1,1160 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file gnunet-namestore-fcfsd.c + * @brief HTTP daemon that offers first-come-first-serve GNS domain registration + * @author Christian Grothoff + */ + +#include "platform.h" +#include +#include "gnunet_util_lib.h" +#include "gnunet_identity_service.h" +#include "gnunet_gnsrecord_lib.h" +#include "gnunet_namestore_service.h" +#include "gnunet_mhd_compat.h" +#include "gnunet_json_lib.h" + +/** + * Structure representing a static page. + * "Static" means that the server does not process the page before sending it + * to the client. Clients can still process the received data, for example + * because there are scripting elements within. + */ +struct StaticPage +{ + /** + * Handle to file on disk. + */ + struct GNUNET_DISK_FileHandle *handle; + + /** + * Size in bytes of the file. + */ + uint64_t size; + + /** + * Cached response object to send to clients. + */ + struct MHD_Response *response; +}; + +/** + * Structure containing some request-specific data. + */ +struct RequestData +{ + /** + * The connection this request was sent in. + */ + struct MHD_Connection *c; + + /** + * Body of the response object. + */ + char *body; + + /** + * Length in bytes of the body. + */ + size_t body_length; + + /** + * Response code. + */ + int code; + + /** + * Task started to search for an entry in the namestore. + */ + struct GNUNET_NAMESTORE_QueueEntry *searching; + + /** + * Task started to iterate over the namestore. + */ + struct GNUNET_NAMESTORE_ZoneIterator *iterating; + + /** + * Pointer used while processing POST data. + */ + void *ptr; + + /** + * Name requested to be registered. + */ + char *register_name; + + /** + * Key (encoded as a string) to be associated with the requested name. + */ + char *register_key; + + /** + * Key to be associated with the requested name. + */ + struct GNUNET_CRYPTO_PublicKey key; +}; + +/** + * Name of the zone being managed. + */ +static char *zone = NULL; + +/** + * The port the daemon is listening to for HTTP requests. + */ +static unsigned long long port = 18080; + +/** + * Connection with the namestore service. + */ +static struct GNUNET_NAMESTORE_Handle *namestore = NULL; + +/** + * Connection with the identity service. + */ +static struct GNUNET_IDENTITY_Handle *identity = NULL; + +/** + * Private key of the zone. + */ +static const struct GNUNET_CRYPTO_PrivateKey *zone_key = NULL; + +/** + * The HTTP daemon. + */ +static struct MHD_Daemon *httpd = NULL; + +/** + * Task executing the HTTP daemon. + */ +static struct GNUNET_SCHEDULER_Task *httpd_task = NULL; + +/** + * The main page, a.k.a. "index.html" + */ +static struct StaticPage *main_page = NULL; + +/** + * Page indicating the requested resource could not be found. + */ +static struct StaticPage *notfound_page = NULL; + +/** + * Page indicating the requested resource could not be accessed, and other + * errors. + */ +static struct StaticPage *forbidden_page = NULL; + +/** + * The relative expiration time for added records + */ +static struct GNUNET_TIME_Relative record_exp; + +/** + * Task ran at shutdown to clean up everything. + * + * @param cls unused + */ +static void +do_shutdown (void *cls) +{ + /* We cheat a bit here: the file descriptor is implicitly closed by MHD, so + calling `GNUNET_DISK_file_close' would generate a spurious warning message + in the log. Since that function does nothing but close the descriptor and + free the allocated memory, After destroying the response all that's left to + do is call `GNUNET_free'. */ + if (NULL != main_page) + { + MHD_destroy_response (main_page->response); + GNUNET_free (main_page->handle); + GNUNET_free (main_page); + } + if (NULL != notfound_page) + { + MHD_destroy_response (notfound_page->response); + GNUNET_free (notfound_page->handle); + GNUNET_free (notfound_page); + } + if (NULL != forbidden_page) + { + MHD_destroy_response (forbidden_page->response); + GNUNET_free (forbidden_page->handle); + GNUNET_free (forbidden_page); + } + + if (NULL != namestore) + { + GNUNET_NAMESTORE_disconnect (namestore); + } + + if (NULL != identity) + { + GNUNET_IDENTITY_disconnect (identity); + } + + if (NULL != httpd_task) + { + GNUNET_SCHEDULER_cancel (httpd_task); + } + if (NULL != httpd) + { + MHD_stop_daemon (httpd); + } +} + + +/** + * Called when the HTTP server has some pending operations. + * + * @param cls unused + */ +static void +do_httpd (void *cls); + +/** + * Schedule a task to run MHD. + */ +static void +run_httpd (void) +{ + fd_set rs; + fd_set ws; + fd_set es; + + struct GNUNET_NETWORK_FDSet *grs = GNUNET_NETWORK_fdset_create (); + struct GNUNET_NETWORK_FDSet *gws = GNUNET_NETWORK_fdset_create (); + struct GNUNET_NETWORK_FDSet *ges = GNUNET_NETWORK_fdset_create (); + + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + + int max = -1; + GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max)); + + unsigned MHD_LONG_LONG timeout = 0; + struct GNUNET_TIME_Relative gtime = GNUNET_TIME_UNIT_FOREVER_REL; + if (MHD_YES == MHD_get_timeout (httpd, &timeout)) + { + gtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + timeout); + } + + GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); + GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); + GNUNET_NETWORK_fdset_copy_native (ges, &es, max + 1); + + httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, + gtime, + grs, + gws, + &do_httpd, + NULL); + GNUNET_NETWORK_fdset_destroy (grs); + GNUNET_NETWORK_fdset_destroy (gws); + GNUNET_NETWORK_fdset_destroy (ges); +} + + +/** + * Called when the HTTP server has some pending operations. + * + * @param cls unused + */ +static void +do_httpd (void *cls) +{ + httpd_task = NULL; + MHD_run (httpd); + run_httpd (); +} + + +static void +run_httpd_now (void) +{ + if (NULL != httpd_task) + { + GNUNET_SCHEDULER_cancel (httpd_task); + httpd_task = NULL; + } + httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL); +} + + +/** + * Generate a JSON object. + * + * @param key the key for the first element + * @param value the value for the first element + * @param ... key-value pairs of the object, terminated by NULL + * @return a JSON string (allocated) + */ +static char * +make_json (const char *key, const char *value, ...) +{ + va_list args; + va_start (args, value); + + json_t *obj = NULL; + + obj = json_object (); + if ((NULL == key) || (NULL == value)) + { + va_end (args); + return json_dumps (obj, JSON_COMPACT); + } + + json_object_set (obj, key, json_string (value)); + + char *k = va_arg (args, char *); + if (NULL == k) + { + va_end (args); + return json_dumps (obj, JSON_COMPACT); + } + char *v = va_arg (args, char *); + if (NULL == v) + { + va_end (args); + return json_dumps (obj, JSON_COMPACT); + } + + while (NULL != k && NULL != v) + { + json_object_set (obj, k, json_string (v)); + k = va_arg (args, char *); + if (NULL != k) + { + v = va_arg (args, char *); + } + } + + va_end (args); + + char *json = json_dumps (obj, JSON_COMPACT); + json_decref (obj); + + return json; +} + + +/** + * The namestore search task failed. + * + * @param cls the request data + */ +static void +search_error_cb (void *cls) +{ + struct RequestData *rd = cls; + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("can not search the namestore"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); +} + + +/** + * The lookup terminated with some results. + * + * @param cls closure + * @param zone the private key of the zone + * @param label the result label + * @param count number of records found + * @param d records found + */ +static void +search_done_cb (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *d) +{ + (void) zone; + (void) d; + + struct RequestData *rd = cls; + MHD_resume_connection (rd->c); + + rd->searching = NULL; + rd->body = make_json ("error", "false", + "free", (0 == count) ? "true" : "false", + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_OK; + + run_httpd_now (); +} + + +/** + * An error occurred while registering a name. + * + * @param cls the connection + */ +static void +register_error_cb (void *cls) +{ + struct RequestData *rd = cls; + + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("unable to scan namestore"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); +} + + +static void +register_done_cb (void *cls, + enum GNUNET_ErrorCode ec) +{ + struct RequestData *rd = cls; + + MHD_resume_connection (rd->c); + rd->searching = NULL; + + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Failed to create record for `%s': %s\n"), + rd->register_name, + GNUNET_ErrorCode_get_hint (ec)); + rd->body = make_json ("error", "true", + "message", + GNUNET_ErrorCode_get_hint (ec), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + } + else + { + rd->body = make_json ("error", "false", + "message", _ ("no errors"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_OK; + } + + run_httpd_now (); +} + + +/** + * Attempt to register the requested name. + * + * @param cls the connection + * @param key the zone key + * @param label name of the record + * @param count number of records found + * @param d records + */ +static void +register_do_cb (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *key, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *d) +{ + (void) key; + (void) d; + + struct RequestData *rd = cls; + + rd->searching = NULL; + + if (0 != count) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("The requested key `%s' exists as `%s'\n"), + rd->register_key, + label); + + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("key exists"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_FORBIDDEN; + run_httpd_now (); + return; + } + + struct GNUNET_GNSRECORD_Data gd; + char *gdraw = NULL; + + if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&(rd->key), + &gdraw, + &(gd.data_size), + &(gd.record_type))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Error creating record data\n")); + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("unable to store record"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); + return; + } + + gd.data = gdraw; + gd.expiration_time = record_exp.rel_value_us; + gd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + + rd->searching = GNUNET_NAMESTORE_records_store (namestore, + zone_key, + rd->register_name, + 1, + &gd, + ®ister_done_cb, + rd); + + GNUNET_free (gdraw); +} + + +/** + * An error occurred while iterating the namestore. + * + * @param cls the connection + */ +static void +iterate_error_cb (void *cls) +{ + struct RequestData *rd = cls; + + MHD_resume_connection (rd->c); + rd->iterating = NULL; + rd->body = make_json ("error", "true", + "message", _ ("unable to scan namestore"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); +} + + +/** + * A block was received from the namestore. + * + * @param cls the connection + * @param key the zone key + * @param label the records' label + * @param count number of records found + * @param d the found records + */ +static void +iterate_do_cb (void *cls, + const struct GNUNET_CRYPTO_PrivateKey *key, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *d) +{ + (void) key; + (void) label; + (void) d; + + struct RequestData *rd = cls; + + if (0 == strcmp (label, rd->register_name)) + { + GNUNET_break (0 != count); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Requested name `%s' exists with `%u' records\n"), + rd->register_name, + count); + + MHD_resume_connection (rd->c); + rd->body = make_json ("error", "true", + "message", _ ("name exists\n"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_FORBIDDEN; + GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating); + run_httpd_now (); + return; + } + + GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1); +} + + +/** + * All entries in the namestore have been iterated over. + * + * @param cls the connection + */ +static void +iterate_done_cb (void *cls) +{ + struct RequestData *rd = cls; + + rd->iterating = NULL; + + /* See if the key was not registered already */ + rd->searching = GNUNET_NAMESTORE_zone_to_name (namestore, + zone_key, + &(rd->key), + ®ister_error_cb, + rd, + ®ister_do_cb, + rd); +} + + +/** + * Generate a response containing JSON and send it to the client. + * + * @param c the connection + * @param body the response body + * @param length the body length in bytes + * @param code the response code + * @return MHD_NO on error + */ +static MHD_RESULT +serve_json (struct MHD_Connection *c, + char *body, + size_t length, + int code) +{ + struct MHD_Response *response = + MHD_create_response_from_buffer (length, + body, + MHD_RESPMEM_PERSISTENT); + MHD_RESULT r = MHD_queue_response (c, code, response); + MHD_destroy_response (response); + return r; +} + + +/** + * Send a response back to a connected client. + * + * @param cls unused + * @param connection the connection with the client + * @param url the requested address + * @param method the HTTP method used + * @param version the protocol version (including the "HTTP/" part) + * @param upload_data data sent with a POST request + * @param upload_data_size length in bytes of the POST data + * @param ptr used to pass data between request handling phases + * @return MHD_NO on error + */ +static MHD_RESULT +create_response (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **ptr) +{ + (void) cls; + (void) version; + + struct RequestData *rd = *ptr; + + if (0 == strcmp (method, MHD_HTTP_METHOD_GET)) + { + /* Handle a previously suspended request */ + if (NULL != rd) + { + return serve_json (rd->c, rd->body, rd->body_length, rd->code); + } + + if (0 == strcmp ("/", url)) + { + return MHD_queue_response (connection, + MHD_HTTP_OK, + main_page->response); + } + + if (0 == strcmp ("/search", url)) + { + const char *name = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "name"); + if (NULL == name) + { + return MHD_queue_response (connection, + MHD_HTTP_BAD_REQUEST, + forbidden_page->response); + } + + MHD_suspend_connection (connection); + rd = GNUNET_new (struct RequestData); + rd->c = connection; + rd->searching = GNUNET_NAMESTORE_records_lookup (namestore, + zone_key, + name, + &search_error_cb, + rd, + &search_done_cb, + rd); + *ptr = rd; + return MHD_YES; + } + + return MHD_queue_response (connection, + MHD_HTTP_NOT_FOUND, + notfound_page->response); + } + + if (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) + { + /* We take a shortcut here by always serving the main page: starting a + namestore lookup, allocating the necessary resources, waiting for the + lookup to complete and then discard everything just because it was a HEAD + and thus only the headers are significative, is an unnecessary waste of + resources. The handling of this method could be smarter, for example by + sending a proper content type header based on the endpoint, but this is + not a service in which HEAD requests are significant, so there's no need + to spend too much time here. */ + return MHD_queue_response (connection, + MHD_HTTP_OK, + main_page->response); + } + + if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) + { + if (0 == strcmp ("/register", url)) + { + /* Handle a previously suspended request */ + if ((NULL != rd) && (NULL != rd->body)) + { + return serve_json (rd->c, rd->body, rd->body_length, rd->code); + } + + if (NULL == rd) + { + rd = GNUNET_new (struct RequestData); + rd->c = connection; + rd->body = NULL; + rd->ptr = NULL; + *ptr = rd; + } + + json_t *json = NULL; + enum GNUNET_JSON_PostResult result = + GNUNET_JSON_post_parser (32 * 1024, + connection, + &(rd->ptr), + upload_data, + upload_data_size, + &json); + + switch (result) + { + case GNUNET_JSON_PR_CONTINUE: + /* Keep processing POST data */ + return MHD_YES; + case GNUNET_JSON_PR_OUT_OF_MEMORY: + case GNUNET_JSON_PR_REQUEST_TOO_LARGE: + rd->body = make_json ("error", "true", + "message", _ ("unable to process submitted data"), + NULL); + rd->body_length = strlen (rd->body); +#ifdef MHD_HTTP_CONTENT_TOO_LARGE + rd->code = MHD_HTTP_CONTENT_TOO_LARGE; +#else + rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE; +#endif + return MHD_YES; + case GNUNET_JSON_PR_JSON_INVALID: + rd->body = make_json ("error", "true", + "message", _ ("the submitted data is invalid"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_BAD_REQUEST; + return MHD_YES; + default: + break; + } + + /* POST data has been read in its entirety */ + + const char *name = json_string_value (json_object_get (json, "name")); + const char *key = json_string_value (json_object_get (json, "key")); + if ((NULL == name) || (NULL == key) || (0 == strlen (name)) || (0 == + strlen ( + key))) + { + json_decref (json); + rd->body = make_json ("error", "true", + "message", _ ("invalid parameters"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_BAD_REQUEST; + return MHD_YES; + } + + rd->register_name = strdup (name); + rd->register_key = strdup (key); + + json_decref (json); + GNUNET_JSON_post_parser_cleanup (rd->ptr); + + if ((NULL != strchr (rd->register_name, '.')) || + (NULL != strchr (rd->register_name, '+'))) + { + rd->body = make_json ("error", "true", + "message", _ ("invalid name"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_BAD_REQUEST; + return MHD_YES; + } + + if (GNUNET_OK != GNUNET_CRYPTO_public_key_from_string (rd->register_key, + &(rd->key))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Unable to parse key %s\n"), + rd->register_key); + + rd->body = make_json ("error", "true", + "message", _ ("unable to parse key"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + return MHD_YES; + } + + MHD_suspend_connection (connection); + /* See if the requested name is free */ + rd->iterating = + GNUNET_NAMESTORE_zone_iteration_start (namestore, + zone_key, + &iterate_error_cb, + rd, + &iterate_do_cb, + rd, + &iterate_done_cb, + rd); + return MHD_YES; + } + + return MHD_queue_response (connection, + MHD_HTTP_FORBIDDEN, + forbidden_page->response); + } + + return MHD_queue_response (connection, + MHD_HTTP_NOT_IMPLEMENTED, + forbidden_page->response); +} + + +/** + * Called when a request is completed. + * + * @param cls unused + * @param connection the connection + * @param ptr connection-specific data + * @param status status code + */ +static void +completed_cb (void *cls, + struct MHD_Connection *connection, + void **ptr, + enum MHD_RequestTerminationCode status) +{ + (void) cls; + (void) connection; + (void) status; + + struct RequestData *rd = *ptr; + + if (NULL == rd) + { + return; + } + + if (NULL == rd->body) + { + GNUNET_free (rd->body); + } + + if (NULL != rd->searching) + { + GNUNET_NAMESTORE_cancel (rd->searching); + } + + if (NULL != rd->register_name) + { + GNUNET_free (rd->register_name); + } + + if (NULL != rd->register_key) + { + GNUNET_free (rd->register_key); + } + + if (NULL != rd->iterating) + { + GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating); + } + + GNUNET_free (rd); +} + + +/** + * Called for each ego provided by the identity service. + * + * @param cls closure + * @param ego the ego + * @param ctx application-provided data for the ego + * @param name the ego name + */ +static void +identity_cb (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *name) +{ + (void) cls; + (void) ctx; + + if ((NULL == name) || (0 != strcmp (name, zone))) + { + return; + } + + if (NULL == ego) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("No ego configured for `fcfsd` subsystem\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + zone_key = GNUNET_IDENTITY_ego_get_private_key (ego); + + int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME; + do + { + httpd = MHD_start_daemon (flags, + (uint16_t) port, + NULL, NULL, + &create_response, NULL, + MHD_OPTION_CONNECTION_LIMIT, 128, + MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1, + MHD_OPTION_CONNECTION_TIMEOUT, 4 * 1024, + MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, + MHD_OPTION_END); + flags = MHD_USE_DEBUG; + } while (NULL == httpd && flags != MHD_USE_DEBUG); + + if (NULL == httpd) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to start HTTP server\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + run_httpd (); +} + + +/** + * Open a file on disk and generate a response object for it. + * + * @param name name of the file to open + * @param basedir directory where the file is located + * @return NULL on error + */ +static struct StaticPage * +open_static_page (const char *name, const char *basedir) +{ + char *fullname = NULL; + GNUNET_asprintf (&fullname, "%s/fcfsd-%s", basedir, name); + + struct GNUNET_DISK_FileHandle *f = + GNUNET_DISK_file_open (fullname, + GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_NONE); + GNUNET_free (fullname); + + if (NULL == f) + { + return NULL; + } + + off_t size = 0; + if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size)) + { + GNUNET_DISK_file_close (f); + return NULL; + } + + struct MHD_Response *response = + MHD_create_response_from_fd64 (size, + f->fd); + + if (NULL == response) + { + GNUNET_DISK_file_close (f); + return NULL; + } + + struct StaticPage *page = GNUNET_new (struct StaticPage); + page->handle = f; + page->size = (uint64_t) size; + page->response = response; + return page; +} + + +/** + * Called after the service is up. + * + * @param cls closure + * @param args remaining command line arguments + * @param cfgfile name of the configuration file + * @param cfg the service configuration + */ +static void +run_service (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + (void) cls; + (void) args; + (void) cfgfile; + + GNUNET_log_setup ("fcfsd", "WARNING", NULL); + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, + "fcfsd", + "RELATIVE_RECORD_EXPIRATION", + &record_exp)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("No expiration specified for records.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, + "fcfsd", + "HTTPPORT", + &port)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("No port specified, using default value\n")); + } + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + + namestore = GNUNET_NAMESTORE_connect (cfg); + if (NULL == namestore) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to connect to namestore\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL); + if (NULL == identity) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to connect to identity\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + char *basedir = NULL; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, + "fcfsd", + "HTMLDIR", + &basedir)) + { + basedir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + } + + main_page = open_static_page ("index.html", basedir); + notfound_page = open_static_page ("notfound.html", basedir); + forbidden_page = open_static_page ("forbidden.html", basedir); + + GNUNET_free (basedir); + + if ((NULL == main_page) || (NULL == notfound_page) || (NULL == + forbidden_page) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to set up the daemon\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } +} + + +/** + * The main function of the fcfs daemon. + * + * @param argc number of arguments from the command line + * @param argv the command line arguments + * @return 0 successful exit, a different value otherwise + */ +int +main (int argc, char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_mandatory + (GNUNET_GETOPT_option_string ('z', + "zone", + "EGO", + gettext_noop ( + "name of the zone managed by FCFSD"), + &zone)), + GNUNET_GETOPT_OPTION_END + }; + + return ((GNUNET_OK == GNUNET_PROGRAM_run (argc, + argv, + "gnunet-namestore-fcfsd", + _ ( + "GNU Name System First-Come-First-Served name registration service"), + options, + &run_service, + NULL)) ? + 0 : + 1); +} diff --git a/src/service/namestore/gnunet-service-namestore.c b/src/service/namestore/gnunet-service-namestore.c new file mode 100644 index 000000000..26de295bf --- /dev/null +++ b/src/service/namestore/gnunet-service-namestore.c @@ -0,0 +1,2752 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012, 2013, 2014, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file namestore/gnunet-service-namestore.c + * @brief namestore for the GNUnet naming system + * @author Matthias Wachs + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_gns_service.h" +#include "gnunet_namestore_service.h" +#include "gnunet_namestore_plugin.h" +#include "gnunet_statistics_service.h" +#include "gnunet_signatures.h" +#include "namestore.h" + +#define LOG_STRERROR_FILE(kind, syscall, filename) \ + GNUNET_log_from_strerror_file (kind, "util", syscall, filename) + +/** + * If a monitor takes more than 1 minute to process an event, print a warning. + */ +#define MONITOR_STALL_WARN_DELAY GNUNET_TIME_UNIT_MINUTES + +/** + * Size of the cache used by #get_nick_record() + */ +#define NC_SIZE 16 + +/** + * A namestore client + */ +struct NamestoreClient; + + +/** + * A namestore iteration operation. + */ +struct ZoneIteration +{ + /** + * Next element in the DLL + */ + struct ZoneIteration *next; + + /** + * Previous element in the DLL + */ + struct ZoneIteration *prev; + + /** + * Namestore client which intiated this zone iteration + */ + struct NamestoreClient *nc; + + /** + * The nick to add to the records + */ + struct GNUNET_GNSRECORD_Data *nick; + + /** + * Key of the zone we are iterating over. + */ + struct GNUNET_CRYPTO_PrivateKey zone; + + /** + * The record set filter + */ + enum GNUNET_GNSRECORD_Filter filter; + + /** + * Last sequence number in the zone iteration used to address next + * result of the zone iteration in the store + * + * Initially set to 0. + * Updated in #zone_iterate_proc() + */ + uint64_t seq; + + /** + * The operation id for the zone iteration in the response for the client + */ + uint32_t request_id; + + /** + * Offset of the zone iteration used to address next result of the zone + * iteration in the store + * + * Initially set to 0 in #handle_iteration_start + * Incremented with by every call to #handle_iteration_next + */ + uint32_t offset; + + /** + * Number of pending cache operations triggered by this zone iteration which we + * need to wait for before allowing the client to continue. + */ + unsigned int cache_ops; + + /** + * Set to #GNUNET_YES if the last iteration exhausted the limit set by the + * client and we should send the #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END + * message and free the data structure once @e cache_ops is zero. + */ + int send_end; +}; + +/** + * A namestore client + */ +struct NamestoreClient +{ + /** + * The client + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Database handle for client + */ + struct GNUNET_NAMESTORE_PluginFunctions *GSN_database; + + /** + * Name of loaded plugin (neeed for cleanup) + */ + char *db_lib_name; + + /** + * GNUNET_YES if this nc has begun a transaction which is uncommited. + */ + int in_transaction; + + /** + * Message queue for transmission to @e client + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Head of the DLL of + * Zone iteration operations in progress initiated by this client + */ + struct ZoneIteration *op_head; + + /** + * Tail of the DLL of + * Zone iteration operations in progress initiated by this client + */ + struct ZoneIteration *op_tail; +}; + + +/** + * A namestore monitor. + */ +struct ZoneMonitor +{ + /** + * Next element in the DLL + */ + struct ZoneMonitor *next; + + /** + * Previous element in the DLL + */ + struct ZoneMonitor *prev; + + /** + * Namestore client which intiated this zone monitor + */ + struct NamestoreClient *nc; + + /** + * Private key of the zone. + */ + struct GNUNET_CRYPTO_PrivateKey zone; + + /** + * The record set filter + */ + enum GNUNET_GNSRECORD_Filter filter; + + /** + * Task active during initial iteration. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Task to warn about slow monitors. + */ + struct GNUNET_SCHEDULER_Task *sa_wait_warning; + + /** + * Since when are we blocked on this monitor? + */ + struct GNUNET_TIME_Absolute sa_waiting_start; + + /** + * Last sequence number in the zone iteration used to address next + * result of the zone iteration in the store + * + * Initially set to 0. + * Updated in #monitor_iterate_cb() + */ + uint64_t seq; + + /** + * Current limit of how many more messages we are allowed + * to queue to this monitor. + */ + uint64_t limit; + + /** + * How many more requests may we receive from the iterator + * before it is at the limit we gave it? Will be below or + * equal to @e limit. The effective limit for monitor + * events is thus @e iteration_cnt - @e limit! + */ + uint64_t iteration_cnt; + + /** + * Are we (still) in the initial iteration pass? + */ + int in_first_iteration; + + /** + * Run again because we skipped an orphan + */ + int run_again; + + /** + * Is there a store activity waiting for this monitor? We only raise the + * flag when it happens and search the DLL for the store activity when we + * had a limit increase. If we cannot find any waiting store activity at + * that time, we clear the flag again. + */ + int sa_waiting; +}; + + + +/** + * Information for an ongoing #handle_record_store() operation. + * Needed as we may wait for monitors to be ready for the notification. + */ +struct StoreActivity +{ + /** + * Kept in a DLL. + */ + struct StoreActivity *next; + + /** + * Kept in a DLL. + */ + struct StoreActivity *prev; + + /** + * Which client triggered the store activity? + */ + struct NamestoreClient *nc; + + /** + * The request ID + */ + uint32_t rid; + + /** + * The currently processed record + */ + uint16_t rd_set_pos; + + /** + * The number of records in this activity + */ + uint16_t rd_set_count; + + /** + * Wheather or not this store action is already commited. + * The store activity will not be processed unless this field is GNUNET_YES + */ + int uncommited; + + /** + * The zone private key + */ + struct GNUNET_CRYPTO_PrivateKey private_key; + + /** + * Copy of the original record set (as data fields in @e rd will + * point into it!). + */ + const struct RecordSet *rs; + + /** + * Next zone monitor that still needs to be notified about this PUT. + */ + struct ZoneMonitor *zm_pos; + +}; + + +/** + * Entry in list of cached nick resolutions. + */ +struct NickCache +{ + /** + * Zone the cache entry is for. + */ + struct GNUNET_CRYPTO_PrivateKey zone; + + /** + * Cached record data. + */ + struct GNUNET_GNSRECORD_Data *rd; + + /** + * Timestamp when this cache entry was used last. + */ + struct GNUNET_TIME_Absolute last_used; +}; + +/** + * We cache nick records to reduce DB load. + */ +static struct NickCache nick_cache[NC_SIZE]; + +/** + * Public key of all zeros. + */ +static const struct GNUNET_CRYPTO_PrivateKey zero; + +/** + * Configuration handle. + */ +static const struct GNUNET_CONFIGURATION_Handle *GSN_cfg; + +/** + * Handle to the statistics service + */ +static struct GNUNET_STATISTICS_Handle *statistics; + +/** + * Name of the database plugin + */ +static char *db_lib_name; + +/** + * Database handle for service + */ +struct GNUNET_NAMESTORE_PluginFunctions *GSN_database; + + +/** + * First active zone monitor. + */ +static struct ZoneMonitor *monitor_head; + +/** + * Last active zone monitor. + */ +static struct ZoneMonitor *monitor_tail; + +/** + * Head of DLL of monitor-blocked store activities. + */ +static struct StoreActivity *sa_head; + +/** + * Tail of DLL of monitor-blocked store activities. + */ +static struct StoreActivity *sa_tail; + +/** + * Notification context shared by all monitors. + */ +static struct GNUNET_NotificationContext *monitor_nc; + +/** + * Returned orphaned records? + */ +static int return_orphaned; + +/** + * Task run during shutdown. + * + * @param cls unused + */ +static void +cleanup_task (void *cls) +{ + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping namestore service\n"); + if (NULL != monitor_nc) + { + GNUNET_notification_context_destroy (monitor_nc); + monitor_nc = NULL; + } + if (NULL != statistics) + { + GNUNET_STATISTICS_destroy (statistics, GNUNET_NO); + statistics = NULL; + } + GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, GSN_database)); + GNUNET_free (db_lib_name); + db_lib_name = NULL; +} + + +/** + * Release memory used by @a sa. + * + * @param sa activity to free + */ +static void +free_store_activity (struct StoreActivity *sa) +{ + GNUNET_CONTAINER_DLL_remove (sa_head, sa_tail, sa); + GNUNET_free (sa); +} + +/** + * Function called with the records for the #GNUNET_GNS_EMPTY_LABEL_AT + * label in the zone. Used to locate the #GNUNET_GNSRECORD_TYPE_NICK + * record, which (if found) is then copied to @a cls for future use. + * + * @param cls a `struct GNUNET_GNSRECORD_Data **` for storing the nick (if found) + * @param seq sequence number of the record, MUST NOT BE ZERO + * @param private_key the private key of the zone (unused) + * @param label should be #GNUNET_GNS_EMPTY_LABEL_AT + * @param rd_count number of records in @a rd + * @param rd records stored under @a label in the zone + */ +static void +lookup_nick_it (void *cls, + uint64_t seq, + const struct GNUNET_CRYPTO_PrivateKey *private_key, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct GNUNET_GNSRECORD_Data **res = cls; + + (void) private_key; + GNUNET_assert (0 != seq); + if (0 != strcmp (label, GNUNET_GNS_EMPTY_LABEL_AT)) + { + GNUNET_break (0); + return; + } + for (unsigned int c = 0; c < rd_count; c++) + { + if (GNUNET_GNSRECORD_TYPE_NICK == rd[c].record_type) + { + (*res) = + GNUNET_malloc (rd[c].data_size + sizeof(struct GNUNET_GNSRECORD_Data)); + (*res)->data = &(*res)[1]; + GNUNET_memcpy ((void *) (*res)->data, rd[c].data, rd[c].data_size); + (*res)->data_size = rd[c].data_size; + (*res)->expiration_time = rd[c].expiration_time; + (*res)->flags = rd[c].flags; + (*res)->record_type = GNUNET_GNSRECORD_TYPE_NICK; + return; + } + } + (*res) = NULL; +} + + +/** + * Add entry to the cache for @a zone and @a nick + * + * @param zone zone key to cache under + * @param nick nick entry to cache + */ +static void +cache_nick (const struct GNUNET_CRYPTO_PrivateKey *zone, + const struct GNUNET_GNSRECORD_Data *nick) +{ + struct NickCache *oldest; + + oldest = NULL; + for (unsigned int i = 0; i < NC_SIZE; i++) + { + struct NickCache *pos = &nick_cache[i]; + + if ((NULL == oldest) || + (oldest->last_used.abs_value_us > pos->last_used.abs_value_us)) + oldest = pos; + if (0 == GNUNET_memcmp (zone, &pos->zone)) + { + oldest = pos; + break; + } + } + GNUNET_free (oldest->rd); + oldest->zone = *zone; + if (NULL != nick) + { + oldest->rd = GNUNET_malloc (sizeof(*nick) + nick->data_size); + *oldest->rd = *nick; + oldest->rd->data = &oldest->rd[1]; + memcpy (&oldest->rd[1], nick->data, nick->data_size); + } + else + { + oldest->rd = NULL; + } + oldest->last_used = GNUNET_TIME_absolute_get (); +} + + +/** + * Return the NICK record for the zone (if it exists). + * + * @param nc the namestore client + * @param zone private key for the zone to look for nick + * @return NULL if no NICK record was found + */ +static struct GNUNET_GNSRECORD_Data * +get_nick_record (const struct GNUNET_CRYPTO_PrivateKey *zone) +{ + struct GNUNET_CRYPTO_PublicKey pub; + struct GNUNET_GNSRECORD_Data *nick; + int res; + + /* check cache first */ + for (unsigned int i = 0; i < NC_SIZE; i++) + { + struct NickCache *pos = &nick_cache[i]; + if ((NULL != pos->rd) && (0 == GNUNET_memcmp (zone, &pos->zone))) + { + if (NULL == pos->rd) + return NULL; + nick = GNUNET_malloc (sizeof(*nick) + pos->rd->data_size); + *nick = *pos->rd; + nick->data = &nick[1]; + memcpy (&nick[1], pos->rd->data, pos->rd->data_size); + pos->last_used = GNUNET_TIME_absolute_get (); + return nick; + } + } + + nick = NULL; + res = GSN_database->lookup_records (GSN_database->cls, + zone, + GNUNET_GNS_EMPTY_LABEL_AT, + &lookup_nick_it, + &nick); + if ((GNUNET_OK != res) || (NULL == nick)) + { +#if ! defined(GNUNET_CULL_LOGGING) + static int do_log = GNUNET_LOG_CALL_STATUS; + + if (0 == do_log) + do_log = GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_DEBUG, + "namestore", + __FILE__, + __FUNCTION__, + __LINE__); + if (1 == do_log) + { + GNUNET_CRYPTO_key_get_public (zone, &pub); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "No nick name set for zone `%s'\n", + GNUNET_GNSRECORD_z2s (&pub)); + } +#endif + /* update cache */ + cache_nick (zone, NULL); + return NULL; + } + + /* update cache */ + cache_nick (zone, nick); + return nick; +} + + +/** + * Merge the nick record @a nick_rd with the rest of the + * record set given in @a rd2. Store the result in @a rdc_res + * and @a rd_res. The @a nick_rd's expiration time is set to + * the maximum expiration time of all of the records in @a rd2. + * + * @param nick_rd the nick record to integrate + * @param rd2_length length of the @a rd2 array + * @param rd2 array of records + * @param[out] rdc_res length of the resulting @a rd_res array + * @param[out] rd_res set to an array of records, + * including @a nick_rd and @a rd2; + * all of the variable-size 'data' fields in @a rd2 are + * allocated in the same chunk of memory! + */ +static void +merge_with_nick_records (const struct GNUNET_GNSRECORD_Data *nick_rd, + unsigned int rd2_length, + const struct GNUNET_GNSRECORD_Data *rd2, + unsigned int *rdc_res, + struct GNUNET_GNSRECORD_Data **rd_res) +{ + uint64_t latest_expiration; + size_t req; + char *data; + size_t data_offset; + struct GNUNET_GNSRECORD_Data *target; + + (*rdc_res) = 1 + rd2_length; + if (0 == 1 + rd2_length) + { + GNUNET_break (0); + (*rd_res) = NULL; + return; + } + req = sizeof(struct GNUNET_GNSRECORD_Data) + nick_rd->data_size; + for (unsigned int i = 0; i < rd2_length; i++) + { + const struct GNUNET_GNSRECORD_Data *orig = &rd2[i]; + + if (req + sizeof(struct GNUNET_GNSRECORD_Data) + orig->data_size < req) + { + GNUNET_break (0); + (*rd_res) = NULL; + return; + } + req += sizeof(struct GNUNET_GNSRECORD_Data) + orig->data_size; + } + target = GNUNET_malloc (req); + (*rd_res) = target; + data = (char *) &target[1 + rd2_length]; + data_offset = 0; + latest_expiration = 0; + for (unsigned int i = 0; i < rd2_length; i++) + { + const struct GNUNET_GNSRECORD_Data *orig = &rd2[i]; + + if (0 != (orig->flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION)) + { + if ((GNUNET_TIME_absolute_get ().abs_value_us + orig->expiration_time) > + latest_expiration) + latest_expiration = orig->expiration_time; + } + else if (orig->expiration_time > latest_expiration) + latest_expiration = orig->expiration_time; + target[i] = *orig; + target[i].data = (void *) &data[data_offset]; + GNUNET_memcpy (&data[data_offset], orig->data, orig->data_size); + data_offset += orig->data_size; + } + /* append nick */ + target[rd2_length] = *nick_rd; + /* Mark as supplemental */ + target[rd2_length].flags = nick_rd->flags | GNUNET_GNSRECORD_RF_SUPPLEMENTAL; + target[rd2_length].expiration_time = latest_expiration; + target[rd2_length].data = (void *) &data[data_offset]; + GNUNET_memcpy (&data[data_offset], nick_rd->data, nick_rd->data_size); + data_offset += nick_rd->data_size; + GNUNET_assert (req == (sizeof(struct GNUNET_GNSRECORD_Data)) * (*rdc_res) + + data_offset); +} + + +/** + * Generate a `struct LookupNameResponseMessage` and send it to the + * given client using the given notification context. + * + * @param nc client to unicast to + * @param request_id request ID to use + * @param zone_key zone key of the zone + * @param name name + * @param rd_count number of records in @a rd + * @param rd array of records + * @param filter record set filter + */ +static int +send_lookup_response_with_filter (struct NamestoreClient *nc, + uint32_t request_id, + const struct + GNUNET_CRYPTO_PrivateKey *zone_key, + const char *name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd, + enum GNUNET_GNSRECORD_Filter filter) +{ + struct GNUNET_MQ_Envelope *env; + struct RecordResultMessage *zir_msg; + struct GNUNET_GNSRECORD_Data *nick; + struct GNUNET_GNSRECORD_Data *res; + struct GNUNET_GNSRECORD_Data rd_nf[rd_count]; + struct GNUNET_TIME_Absolute block_exp = GNUNET_TIME_UNIT_ZERO_ABS;; + unsigned int res_count; + unsigned int rd_nf_count; + size_t name_len; + size_t key_len; + ssize_t rd_ser_len; + char *name_tmp; + char *rd_ser; + char *emsg; + + nick = get_nick_record (zone_key); + GNUNET_assert (-1 != GNUNET_GNSRECORD_records_get_size (rd_count, rd)); + + if (GNUNET_OK != GNUNET_GNSRECORD_normalize_record_set (name, + rd, + rd_count, + rd_nf, + &rd_nf_count, + &block_exp, + filter, + &emsg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg); + GNUNET_free (emsg); + GNUNET_assert (0); + } + + /** + * FIXME if we ever support GNUNET_NAMESTORE_OMIT_PUBLIC, + * we need to omit adding this public record here + */ + if ((NULL != nick) && (0 != strcmp (name, GNUNET_GNS_EMPTY_LABEL_AT))) + { + nick->flags = + (nick->flags | GNUNET_GNSRECORD_RF_PRIVATE) ^ GNUNET_GNSRECORD_RF_PRIVATE; + merge_with_nick_records (nick, rd_nf_count, rd_nf, &res_count, &res); + } + else + { + res_count = rd_nf_count; + res = (struct GNUNET_GNSRECORD_Data *) rd_nf; + } + if (NULL != nick) + GNUNET_free (nick); + + if (0 == res_count) + { + if (rd_nf != res) + GNUNET_free (res); + return 0; + } + GNUNET_assert (-1 != GNUNET_GNSRECORD_records_get_size (res_count, res)); + + + name_len = strlen (name) + 1; + rd_ser_len = GNUNET_GNSRECORD_records_get_size (res_count, res); + if (rd_ser_len < 0) + { + if (rd_nf != res) + GNUNET_free (res); + GNUNET_break (0); + GNUNET_SERVICE_client_drop (nc->client); + return 0; + } + if (((size_t) rd_ser_len) >= UINT16_MAX - name_len - sizeof(*zir_msg)) + { + if (rd_nf != res) + GNUNET_free (res); + GNUNET_break (0); + GNUNET_SERVICE_client_drop (nc->client); + return 0; + } + key_len = GNUNET_CRYPTO_private_key_get_length (zone_key); + env = GNUNET_MQ_msg_extra (zir_msg, + name_len + rd_ser_len + key_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT); + zir_msg->gns_header.r_id = htonl (request_id); + zir_msg->name_len = htons (name_len); + zir_msg->rd_count = htons (res_count); + zir_msg->rd_len = htons ((uint16_t) rd_ser_len); + zir_msg->key_len = htons (key_len); + GNUNET_CRYPTO_write_private_key_to_buffer (zone_key, + &zir_msg[1], + key_len); + zir_msg->expire = GNUNET_TIME_absolute_hton (block_exp); + name_tmp = (char *) &zir_msg[1] + key_len; + GNUNET_memcpy (name_tmp, name, name_len); + rd_ser = &name_tmp[name_len]; + GNUNET_assert ( + rd_ser_len == + GNUNET_GNSRECORD_records_serialize (res_count, res, rd_ser_len, rd_ser)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending RECORD_RESULT message with %u records\n", + res_count); + GNUNET_STATISTICS_update (statistics, + "Record sets sent to clients", + 1, + GNUNET_NO); + GNUNET_MQ_send (nc->mq, env); + if (rd_nf != res) + GNUNET_free (res); + return res_count; +} + +/** + * Send response to the store request to the client. + * + * @param nc client to talk to + * @param ec status of the operation + * @param rid client's request ID + */ +static void +send_store_response (struct NamestoreClient *nc, + enum GNUNET_ErrorCode ec, + uint32_t rid) +{ + struct GNUNET_MQ_Envelope *env; + struct RecordStoreResponseMessage *rcr_msg; + + GNUNET_assert (NULL != nc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending RECORD_STORE_RESPONSE message\n"); + GNUNET_STATISTICS_update (statistics, + "Store requests completed", + 1, + GNUNET_NO); + env = GNUNET_MQ_msg (rcr_msg, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE); + rcr_msg->gns_header.r_id = htonl (rid); + rcr_msg->ec = htonl (ec); + GNUNET_MQ_send (nc->mq, env); +} + + +/** + * Function called once we are done with the zone iteration and + * allow the zone iteration client to send us more messages. + * + * @param zi zone iteration we are processing + */ +static void +zone_iteration_done_client_continue (struct ZoneIteration *zi) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_NAMESTORE_Header *em; + + GNUNET_SERVICE_client_continue (zi->nc->client); + if (! zi->send_end) + return; + /* send empty response to indicate end of list */ + env = GNUNET_MQ_msg (em, GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END); + em->r_id = htonl (zi->request_id); + GNUNET_MQ_send (zi->nc->mq, env); + + GNUNET_CONTAINER_DLL_remove (zi->nc->op_head, zi->nc->op_tail, zi); + GNUNET_free (zi); +} + + + + +/** + * Print a warning that one of our monitors is no longer reacting. + * + * @param cls a `struct ZoneMonitor` to warn about + */ +static void +warn_monitor_slow (void *cls) +{ + struct ZoneMonitor *zm = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No response from monitor since %s\n", + GNUNET_STRINGS_absolute_time_to_string (zm->sa_waiting_start)); + zm->sa_wait_warning = GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, + &warn_monitor_slow, + zm); +} + + +/** + * Continue processing the @a sa. + * + * @param sa store activity to process + */ +static int +continue_store_activity (struct StoreActivity *sa, + int call_continue) +{ + const struct RecordSet *rd_set = sa->rs; + unsigned int rd_count; + size_t name_len; + size_t rd_ser_len; + const char *name_tmp; + const char *rd_ser; + const char *buf; + char *conv_name; + + // If we are in a transaction, do not notify monitors or update + // cached. This will be done when we are commiting. + if (GNUNET_YES == sa->uncommited) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transaction not yet committed, delaying monitor and cache updates\n"); + send_store_response (sa->nc, GNUNET_EC_NONE, sa->rid); + if (GNUNET_YES == call_continue) + GNUNET_SERVICE_client_continue (sa->nc->client); + return GNUNET_OK; + } + buf = (const char *) &sa[1]; + for (int i = sa->rd_set_pos; i < sa->rd_set_count; i++) + { + rd_set = (struct RecordSet *) buf; + name_len = ntohs (rd_set->name_len); + rd_count = ntohs (rd_set->rd_count); + rd_ser_len = ntohs (rd_set->rd_len); + name_tmp = (const char *) &rd_set[1]; + rd_ser = &name_tmp[name_len]; + conv_name = GNUNET_GNSRECORD_string_normalize (name_tmp); + GNUNET_assert (NULL != conv_name); + { + struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; + + /* We did this before, must succeed again */ + GNUNET_assert ( + GNUNET_OK == + GNUNET_GNSRECORD_records_deserialize (rd_ser_len, rd_ser, rd_count, + rd)); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Checking monitors watching for `%s'\n", + conv_name); + for (struct ZoneMonitor *zm = sa->zm_pos; NULL != zm; zm = sa->zm_pos) + { + if ((0 != GNUNET_memcmp (&sa->private_key, &zm->zone)) && + (0 != GNUNET_memcmp (&zm->zone, &zero))) + { + sa->zm_pos = zm->next; /* not interesting to this monitor */ + continue; + } + if (zm->limit == zm->iteration_cnt) + { + zm->sa_waiting = GNUNET_YES; + zm->sa_waiting_start = GNUNET_TIME_absolute_get (); + if (NULL != zm->sa_wait_warning) + GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); + zm->sa_wait_warning = + GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, + &warn_monitor_slow, + zm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Monitor is blocking client for `%s'\n", + conv_name); + GNUNET_free (conv_name); + return GNUNET_NO; /* blocked on zone monitor */ + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying monitor about changes under label `%s'\n", + conv_name); + if (0 < send_lookup_response_with_filter (zm->nc, + 0, + &sa->private_key, + conv_name, + rd_count, + rd, + zm->filter)) + zm->limit--; + sa->zm_pos = zm->next; + } + sa->rd_set_pos++; + GNUNET_free (conv_name); + } + } + if (GNUNET_YES == call_continue) + GNUNET_SERVICE_client_continue (sa->nc->client); + send_store_response (sa->nc, GNUNET_EC_NONE, sa->rid); + free_store_activity (sa); + return GNUNET_OK; +} + + +/** + * Called whenever a client is disconnected. + * Frees our resources associated with that client. + * + * @param cls closure + * @param client identification of the client + * @param app_ctx the `struct NamestoreClient` of @a client + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *app_ctx) +{ + struct NamestoreClient *nc = app_ctx; + struct ZoneIteration *no; + struct StoreActivity *sa = sa_head; + struct StoreActivity *sn; + char *emsg; + + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p disconnected\n", client); + if (GNUNET_YES == nc->in_transaction) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Client in transaction, rolling back...\n"); + if (GNUNET_SYSERR == nc->GSN_database->transaction_rollback ( + nc->GSN_database->cls, + &emsg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to roll back: %s\n", emsg); + GNUNET_free (emsg); + } + else + { + nc->in_transaction = GNUNET_NO; + while (NULL != sa) + { + if ((nc != sa->nc) || + (GNUNET_NO == sa->uncommited)) + { + sa = sa->next; + continue; + } + sn = sa->next; + free_store_activity (sa); + sa = sn; + } + } + } + for (struct ZoneMonitor *zm = monitor_head; NULL != zm; zm = zm->next) + { + if (nc != zm->nc) + continue; + GNUNET_CONTAINER_DLL_remove (monitor_head, monitor_tail, zm); + if (NULL != zm->task) + { + GNUNET_SCHEDULER_cancel (zm->task); + zm->task = NULL; + } + if (NULL != zm->sa_wait_warning) + { + GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); + zm->sa_wait_warning = NULL; + } + for (sa = sa_head; NULL != sa; sa = sn) + { + sn = sa->next; + if (zm == sa->zm_pos) + { + sa->zm_pos = zm->next; + /* this may free sa */ + continue_store_activity (sa, GNUNET_YES); + } + } + GNUNET_free (zm); + break; + } + sa = sa_head; + while (NULL != sa) + { + if (nc != sa->nc) + { + sa = sa->next; + continue; + } + sn = sa->next; + free_store_activity (sa); + sa = sn; + } + while (NULL != (no = nc->op_head)) + { + GNUNET_CONTAINER_DLL_remove (nc->op_head, nc->op_tail, no); + GNUNET_free (no); + } + GNUNET_break (NULL == GNUNET_PLUGIN_unload (nc->db_lib_name, + nc->GSN_database)); + GNUNET_free (nc->db_lib_name); + GNUNET_free (nc); +} + + +/** + * Add a client to our list of active clients. + * + * @param cls NULL + * @param client client to add + * @param mq message queue for @a client + * @return internal namestore client structure for this client + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) +{ + struct NamestoreClient *nc; + char *database; + + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected\n", client); + nc = GNUNET_new (struct NamestoreClient); + nc->client = client; + nc->mq = mq; + /* Loading database plugin */ + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (GSN_cfg, + "namestore", + "database", + &database)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n"); + GNUNET_free (nc); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading %s\n", db_lib_name); + nc->GSN_database = GNUNET_PLUGIN_load (db_lib_name, (void *) GSN_cfg); + GNUNET_free (database); + if (NULL == nc->GSN_database) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not load database backend `%s'\n", + db_lib_name); + GNUNET_free (nc); + return NULL; + } + nc->db_lib_name = GNUNET_strdup (db_lib_name); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loaded %s\n", db_lib_name); + return nc; +} + + +/** + * Closure for #lookup_it(). + */ +struct RecordLookupContext +{ + /** + * The label to look up. + */ + const char *label; + + /** + * The record result. + */ + char *res_rd; + + /** + * The nick for the zone. + */ + struct GNUNET_GNSRECORD_Data *nick; + + /** + * If a record set was found or not. + */ + int found; + + /** + * The record filter + */ + enum GNUNET_GNSRECORD_Filter filter; + + /** + * The number of found records. + */ + unsigned int res_rd_count; + + /** + * The length of the serialized records. + */ + ssize_t rd_ser_len; +}; + + +/** + * Function called by the namestore plugin when we are trying to lookup + * a record as part of #handle_record_lookup(). Merges all results into + * the context. + * + * @param cls closure with a `struct RecordLookupContext` + * @param seq unique serial number of the record, MUST NOT BE ZERO + * @param private_key private key of the zone + * @param label name that is being mapped (at most 255 characters long) + * @param rd_count number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +lookup_it (void *cls, + uint64_t seq, + const struct GNUNET_CRYPTO_PrivateKey *private_key, + const char *label, + unsigned int rd_count_nf, + const struct GNUNET_GNSRECORD_Data *rd_nf) +{ + struct RecordLookupContext *rlc = cls; + struct GNUNET_GNSRECORD_Data rd[rd_count_nf]; + struct GNUNET_TIME_Absolute block_exp; + unsigned int rd_count = 0; + char *emsg; + + (void) private_key; + GNUNET_assert (0 != seq); + if (0 != strcmp (label, rlc->label)) + return; + rlc->found = GNUNET_YES; + + if (GNUNET_OK != GNUNET_GNSRECORD_normalize_record_set (rlc->label, + rd_nf, + rd_count_nf, + rd, + &rd_count, + &block_exp, + rlc->filter, + &emsg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg); + GNUNET_free (emsg); + GNUNET_assert (0); + } + + if (0 == rd_count) + { + rlc->rd_ser_len = 0; + rlc->res_rd_count = 0; + rlc->res_rd = NULL; + return; + } + if ((NULL != rlc->nick) && (0 != strcmp (label, GNUNET_GNS_EMPTY_LABEL_AT))) + { + /* Merge */ + struct GNUNET_GNSRECORD_Data *rd_res; + unsigned int rdc_res; + + rd_res = NULL; + rdc_res = 0; + rlc->nick->flags = (rlc->nick->flags | GNUNET_GNSRECORD_RF_PRIVATE) + ^ GNUNET_GNSRECORD_RF_PRIVATE; + merge_with_nick_records (rlc->nick, rd_count, rd, &rdc_res, &rd_res); + rlc->rd_ser_len = GNUNET_GNSRECORD_records_get_size (rdc_res, rd_res); + if (rlc->rd_ser_len < 0) + { + GNUNET_break (0); + GNUNET_free (rd_res); + rlc->found = GNUNET_NO; + rlc->rd_ser_len = 0; + return; + } + rlc->res_rd_count = rdc_res; + rlc->res_rd = GNUNET_malloc (rlc->rd_ser_len); + if (rlc->rd_ser_len != GNUNET_GNSRECORD_records_serialize (rdc_res, + rd_res, + rlc->rd_ser_len, + rlc->res_rd)) + { + GNUNET_break (0); + GNUNET_free (rlc->res_rd); + rlc->res_rd = NULL; + rlc->res_rd_count = 0; + rlc->rd_ser_len = 0; + GNUNET_free (rd_res); + rlc->found = GNUNET_NO; + return; + } + GNUNET_free (rd_res); + GNUNET_free (rlc->nick); + rlc->nick = NULL; + } + else + { + rlc->rd_ser_len = GNUNET_GNSRECORD_records_get_size (rd_count, rd); + if (rlc->rd_ser_len < 0) + { + GNUNET_break (0); + rlc->found = GNUNET_NO; + rlc->rd_ser_len = 0; + return; + } + rlc->res_rd_count = rd_count; + rlc->res_rd = GNUNET_malloc (rlc->rd_ser_len); + if (rlc->rd_ser_len != GNUNET_GNSRECORD_records_serialize (rd_count, + rd, + rlc->rd_ser_len, + rlc->res_rd)) + { + GNUNET_break (0); + GNUNET_free (rlc->res_rd); + rlc->res_rd = NULL; + rlc->res_rd_count = 0; + rlc->rd_ser_len = 0; + rlc->found = GNUNET_NO; + return; + } + } +} + + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP message + * + * @param cls client sending the message + * @param ll_msg message of type `struct LabelLookupMessage` + * @return #GNUNET_OK if @a ll_msg is well-formed + */ +static int +check_record_lookup (void *cls, const struct LabelLookupMessage *ll_msg) +{ + uint32_t name_len; + size_t src_size; + size_t key_len; + + (void) cls; + name_len = ntohs (ll_msg->label_len); + key_len = ntohs (ll_msg->key_len); + src_size = ntohs (ll_msg->gns_header.header.size); + if (name_len + key_len != src_size - sizeof(struct LabelLookupMessage)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP message + * + * @param cls client sending the message + * @param ll_msg message of type `struct LabelLookupMessage` + */ +static void +handle_record_lookup (void *cls, const struct LabelLookupMessage *ll_msg) +{ + struct GNUNET_CRYPTO_PrivateKey zone; + struct NamestoreClient *nc = cls; + struct GNUNET_MQ_Envelope *env; + struct LabelLookupResponseMessage *llr_msg; + struct RecordLookupContext rlc; + const char *name_tmp; + char *res_name; + char *conv_name; + uint32_t name_len; + int res; + size_t key_len; + size_t kb_read; + + key_len = ntohs (ll_msg->key_len); + if ((GNUNET_SYSERR == + GNUNET_CRYPTO_read_private_key_from_buffer (&ll_msg[1], + key_len, + &zone, + &kb_read)) || + (kb_read != key_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error reading private key\n"); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + name_tmp = (const char *) &ll_msg[1] + key_len; + GNUNET_SERVICE_client_continue (nc->client); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received NAMESTORE_RECORD_LOOKUP message for name `%s'\n", + name_tmp); + + conv_name = GNUNET_GNSRECORD_string_normalize (name_tmp); + if (NULL == conv_name) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error converting name `%s'\n", + name_tmp); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + name_len = strlen (conv_name) + 1; + rlc.label = conv_name; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Looking up with filter %u\n", ntohs (ll_msg->filter)); + rlc.filter = ntohs (ll_msg->filter); + rlc.found = GNUNET_NO; + rlc.res_rd_count = 0; + rlc.res_rd = NULL; + rlc.rd_ser_len = 0; + rlc.nick = get_nick_record (&zone); + if (GNUNET_YES != ntohs (ll_msg->is_edit_request)) + res = nc->GSN_database->lookup_records (nc->GSN_database->cls, + &zone, + conv_name, + &lookup_it, + &rlc); + else + res = nc->GSN_database->edit_records (nc->GSN_database->cls, + &zone, + conv_name, + &lookup_it, + &rlc); + + env = + GNUNET_MQ_msg_extra (llr_msg, + key_len + name_len + rlc.rd_ser_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE); + llr_msg->gns_header.r_id = ll_msg->gns_header.r_id; + GNUNET_memcpy (&llr_msg[1], &ll_msg[1], key_len); + llr_msg->key_len = ll_msg->key_len; + llr_msg->name_len = htons (name_len); + llr_msg->rd_count = htons (rlc.res_rd_count); + llr_msg->rd_len = htons (rlc.rd_ser_len); + llr_msg->reserved = htons (0); + res_name = ((char *) &llr_msg[1]) + key_len; + if (GNUNET_YES == rlc.found) + llr_msg->found = htons (GNUNET_YES); + else if (GNUNET_SYSERR == res) + llr_msg->found = htons (GNUNET_SYSERR); + else + llr_msg->found = htons (GNUNET_NO); + GNUNET_memcpy (res_name, conv_name, name_len); + GNUNET_memcpy (&res_name[name_len], rlc.res_rd, rlc.rd_ser_len); + GNUNET_MQ_send (nc->mq, env); + GNUNET_free (rlc.res_rd); + GNUNET_free (conv_name); +} + + + +/** + * Checks a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE message + * + * @param cls client sending the message + * @param rp_msg message of type `struct RecordStoreMessage` + * @return #GNUNET_OK if @a rp_msg is well-formed + */ +static int +check_record_store (void *cls, const struct RecordStoreMessage *rp_msg) +{ + size_t msg_size; + size_t min_size_exp; + size_t rd_set_count; + size_t key_len; + + (void) cls; + msg_size = ntohs (rp_msg->gns_header.header.size); + rd_set_count = ntohs (rp_msg->rd_set_count); + key_len = ntohs (rp_msg->key_len); + + min_size_exp = sizeof(*rp_msg) + key_len + sizeof (struct RecordSet) + * rd_set_count; + if (msg_size < min_size_exp) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + +struct LookupExistingRecordsContext +{ + + /** + * The expiration of the existing records or tombstone + */ + struct GNUNET_TIME_Absolute exp; + + /** + * Whether the existing record set consists only of a tombstone + * (e.g. is "empty") + */ + int only_tombstone; + +}; + + +/** + * Check if set contains a tombstone, store if necessary + * + * @param cls a `struct GNUNET_GNSRECORD_Data **` for storing the nick (if found) + * @param seq sequence number of the record, MUST NOT BE ZERO + * @param private_key the private key of the zone (unused) + * @param label should be #GNUNET_GNS_EMPTY_LABEL_AT + * @param rd_count number of records in @a rd + * @param rd records stored under @a label in the zone + */ +static void +get_existing_rd_exp (void *cls, + uint64_t seq, + const struct + GNUNET_CRYPTO_PrivateKey *private_key, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct LookupExistingRecordsContext *lctx = cls; + struct GNUNET_GNSRECORD_Data rd_pub[rd_count]; + unsigned int rd_pub_count; + char *emsg; + + if ((1 == rd_count) && + (GNUNET_GNSRECORD_TYPE_TOMBSTONE == rd[0].record_type)) + { + /* This record set contains only a tombstone! */ + lctx->only_tombstone = GNUNET_YES; + } + if (GNUNET_OK != + GNUNET_GNSRECORD_normalize_record_set (label, + rd, + rd_count, + rd_pub, + &rd_pub_count, + &lctx->exp, + GNUNET_GNSRECORD_FILTER_OMIT_PRIVATE, + &emsg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "%s\n", emsg); + GNUNET_free (emsg); + } +} + +static enum GNUNET_ErrorCode +store_record_set (struct NamestoreClient *nc, + const struct GNUNET_CRYPTO_PrivateKey *private_key, + const struct RecordSet *rd_set, + ssize_t *len) +{ + size_t name_len; + size_t rd_ser_len; + const char *name_tmp; + const char *rd_ser; + char *conv_name; + char *emsg; + unsigned int rd_count; + int res; + enum GNUNET_ErrorCode ec; + struct GNUNET_TIME_Absolute new_block_exp; + struct LookupExistingRecordsContext lctx; + *len = sizeof (struct RecordSet); + + ec = GNUNET_EC_NONE; + lctx.only_tombstone = GNUNET_NO; + lctx.exp = GNUNET_TIME_UNIT_ZERO_ABS; + new_block_exp = GNUNET_TIME_UNIT_ZERO_ABS; + name_len = ntohs (rd_set->name_len); + *len += name_len; + rd_count = ntohs (rd_set->rd_count); + rd_ser_len = ntohs (rd_set->rd_len); + *len += rd_ser_len; + name_tmp = (const char *) &rd_set[1]; + rd_ser = &name_tmp[name_len]; + { + struct GNUNET_GNSRECORD_Data rd[GNUNET_NZL (rd_count)]; + + /* Extracting and converting private key */ + conv_name = GNUNET_GNSRECORD_string_normalize (name_tmp); + if (NULL == conv_name) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error normalizing name `%s'\n", + name_tmp); + return GNUNET_EC_NAMESTORE_LABEL_INVALID; + } + + /* Check name for validity */ + if (GNUNET_OK != GNUNET_GNSRECORD_label_check (conv_name, &emsg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Label invalid: `%s'\n", + emsg); + GNUNET_free (emsg); + GNUNET_free (conv_name); + return GNUNET_EC_NAMESTORE_LABEL_INVALID; + } + + if (GNUNET_OK != + GNUNET_GNSRECORD_records_deserialize (rd_ser_len, rd_ser, rd_count, + rd)) + { + GNUNET_free (conv_name); + return GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; + } + + GNUNET_STATISTICS_update (statistics, + "Well-formed store requests received", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating %u records for name `%s'\n", + (unsigned int) rd_count, + conv_name); + if ((GNUNET_NO == nc->GSN_database->lookup_records (nc->GSN_database->cls, + private_key, + conv_name, + &get_existing_rd_exp, + &lctx)) + && + (rd_count == 0)) + { + /* This name does not exist, so cannot be removed */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Name `%s' does not exist, no deletion required\n", + conv_name); + res = GNUNET_NO; + ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND; + } + else + { + /* remove "NICK" records, unless this is for the + #GNUNET_GNS_EMPTY_LABEL_AT label + We may need one additional record later for tombstone. + FIXME: Since we must normalize the record set (check for + consistency etc) we have to iterate the set twice. + May be inefficient. + We cannot really move the nick caching into GNSRECORD. + */ + struct GNUNET_GNSRECORD_Data rd_clean[GNUNET_NZL (rd_count)]; + struct GNUNET_GNSRECORD_Data rd_nf[GNUNET_NZL (rd_count) + 1]; + unsigned int rd_clean_off; + unsigned int rd_nf_count; + int have_nick; + + rd_clean_off = 0; + have_nick = GNUNET_NO; + for (unsigned int i = 0; i < rd_count; i++) + { + rd_clean[rd_clean_off] = rd[i]; + + if ((0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, conv_name)) || + (GNUNET_GNSRECORD_TYPE_NICK != rd[i].record_type)) + rd_clean_off++; + + if ((0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, conv_name)) && + (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type)) + { + // FIXME: In case this is an uncommited transaction, + // we should not do this here. Can we do this in the store activity? + cache_nick (private_key, &rd[i]); + have_nick = GNUNET_YES; + } + } + if (GNUNET_OK != + GNUNET_GNSRECORD_normalize_record_set (conv_name, + rd_clean, + rd_clean_off, + rd_nf, + &rd_nf_count, + &new_block_exp, + GNUNET_GNSRECORD_FILTER_NONE, + &emsg)) + { + GNUNET_free (conv_name); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Error normalizing record set: `%s'\n", + emsg); + GNUNET_free (emsg); + return GNUNET_EC_NAMESTORE_RECORD_DATA_INVALID; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u/%u records before tombstone\n", rd_nf_count, + rd_clean_off); + /* + * If existing_block_exp is 0, then there was no record set + * and no tombstone. + * Otherwise, if the existing block expiration is after the + * new block expiration would be, we need to add a tombstone + * or update it. + */ + if (GNUNET_TIME_absolute_cmp (new_block_exp, <=, lctx.exp)) + { + rd_nf[rd_nf_count].record_type = GNUNET_GNSRECORD_TYPE_TOMBSTONE; + rd_nf[rd_nf_count].expiration_time = + lctx.exp.abs_value_us; + rd_nf[rd_nf_count].data = NULL; + rd_nf[rd_nf_count].data_size = 0; + rd_nf[rd_nf_count].flags = GNUNET_GNSRECORD_RF_PRIVATE; + rd_nf_count++; + } + if ((0 == strcmp (GNUNET_GNS_EMPTY_LABEL_AT, conv_name)) && + (GNUNET_NO == have_nick)) + { + /* remove nick record from cache, in case we have one there */ + // FIXME: In case this is an uncommited transaction, + // we should not do this here. Can we do this in the store activity? + cache_nick (private_key, NULL); + } + res = nc->GSN_database->store_records (nc->GSN_database->cls, + private_key, + conv_name, + rd_nf_count, + rd_nf); + /* If after a store there is only a TOMBSTONE left, and + * there was >1 record under this label found (the tombstone; indicated + * through res != GNUNET_NO) then we should return "NOT FOUND" == GNUNET_NO + */ + if ((GNUNET_SYSERR != res) && + (0 == rd_count) && + (1 == rd_nf_count) && + (GNUNET_GNSRECORD_TYPE_TOMBSTONE == rd_nf[0].record_type) && + (GNUNET_YES == lctx.only_tombstone)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client tried to remove non-existant record\n"); + ec = GNUNET_EC_NAMESTORE_RECORD_NOT_FOUND; + } + } + + if (GNUNET_SYSERR == res) + { + /* store not successful, no need to tell monitors */ + GNUNET_free (conv_name); + return GNUNET_EC_NAMESTORE_STORE_FAILED; + } + } + GNUNET_free (conv_name); + return ec; +} + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE message + * + * @param cls client sending the message + * @param rp_msg message of type `struct RecordStoreMessage` + */ +static void +handle_record_store (void *cls, const struct RecordStoreMessage *rp_msg) +{ + struct GNUNET_CRYPTO_PrivateKey zone; + struct NamestoreClient *nc = cls; + uint32_t rid; + uint16_t rd_set_count; + const char *buf; + ssize_t read; + size_t key_len; + size_t kb_read; + size_t rp_msg_len; + size_t rs_len; + size_t rs_off; + struct StoreActivity *sa; + struct RecordSet *rs; + enum GNUNET_ErrorCode res; + + key_len = ntohs (rp_msg->key_len); + rp_msg_len = ntohs (rp_msg->gns_header.header.size); + rs_off = sizeof (*rp_msg) + key_len; + rs_len = rp_msg_len - rs_off; + if ((GNUNET_SYSERR == + GNUNET_CRYPTO_read_private_key_from_buffer (&rp_msg[1], + key_len, + &zone, + &kb_read)) || + (kb_read != key_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error reading private key\n"); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received NAMESTORE_RECORD_STORE message\n"); + rid = ntohl (rp_msg->gns_header.r_id); + rd_set_count = ntohs (rp_msg->rd_set_count); + buf = (const char *) rp_msg + rs_off; + for (int i = 0; i < rd_set_count; i++) + { + rs = (struct RecordSet *) buf; + res = store_record_set (nc, &zone, + rs, &read); + if (GNUNET_EC_NONE != res) + { + send_store_response (nc, res, rid); + GNUNET_SERVICE_client_continue (nc->client); + return; + } + buf += read; + } + sa = GNUNET_malloc (sizeof(struct StoreActivity) + rs_len); + GNUNET_CONTAINER_DLL_insert (sa_head, sa_tail, sa); + sa->nc = nc; + sa->rs = (struct RecordSet *) &sa[1]; + sa->rd_set_count = rd_set_count; + GNUNET_memcpy (&sa[1], (char *) rp_msg + rs_off, rs_len); + sa->rid = rid; + sa->rd_set_pos = 0; + sa->private_key = zone; + sa->zm_pos = monitor_head; + sa->uncommited = nc->in_transaction; + continue_store_activity (sa, GNUNET_YES); +} + +static void +send_tx_response (int rid, enum GNUNET_ErrorCode ec, struct NamestoreClient *nc) +{ + struct TxControlResultMessage *txr_msg; + struct GNUNET_MQ_Envelope *env; + + env = + GNUNET_MQ_msg (txr_msg, GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL_RESULT); + txr_msg->gns_header.r_id = rid; + txr_msg->ec = htonl (ec); + GNUNET_MQ_send (nc->mq, env); +} + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL message + * + * @param cls client sending the message + * @param tx_msg message of type `struct TxControlMessage` + */ +static void +handle_tx_control (void *cls, const struct TxControlMessage *tx_msg) +{ + struct NamestoreClient *nc = cls; + struct StoreActivity *sa = sa_head; + struct StoreActivity *sn; + enum GNUNET_GenericReturnValue ret; + char *emsg = NULL; + int blocked = GNUNET_NO; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received TX_CONTROL message\n"); + + switch (ntohs (tx_msg->control)) + { + case GNUNET_NAMESTORE_TX_BEGIN: + ret = nc->GSN_database->transaction_begin (nc->GSN_database->cls, + &emsg); + send_tx_response (tx_msg->gns_header.r_id, + (GNUNET_SYSERR == ret) ? + GNUNET_EC_NAMESTORE_BACKEND_FAILED : GNUNET_EC_NONE, nc); + if (GNUNET_SYSERR == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Databse backend error: `%s'", emsg); + GNUNET_free (emsg); + } + GNUNET_SERVICE_client_continue (nc->client); + nc->in_transaction = GNUNET_YES; + break; + case GNUNET_NAMESTORE_TX_COMMIT: + ret = nc->GSN_database->transaction_commit (nc->GSN_database->cls, + &emsg); + send_tx_response (tx_msg->gns_header.r_id, + (GNUNET_SYSERR == ret) ? + GNUNET_EC_NAMESTORE_BACKEND_FAILED : GNUNET_EC_NONE, + nc); + if (GNUNET_SYSERR == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Databse backend error: `%s'", emsg); + GNUNET_free (emsg); + } + if (GNUNET_SYSERR != ret) + { + nc->in_transaction = GNUNET_NO; + while (NULL != sa) + { + if ((nc != sa->nc) || + (GNUNET_NO == sa->uncommited)) + { + sa = sa->next; + continue; + } + sa->uncommited = GNUNET_NO; + sn = sa->next; + if (GNUNET_OK != continue_store_activity (sa, GNUNET_NO)) + blocked = GNUNET_YES; + sa = sn; + } + if (GNUNET_YES != blocked) + GNUNET_SERVICE_client_continue (nc->client); + } + break; + case GNUNET_NAMESTORE_TX_ROLLBACK: + ret = nc->GSN_database->transaction_rollback (nc->GSN_database->cls, + &emsg); + send_tx_response (tx_msg->gns_header.r_id, + (GNUNET_SYSERR == ret) ? + GNUNET_EC_NAMESTORE_BACKEND_FAILED : GNUNET_EC_NONE, nc); + GNUNET_SERVICE_client_continue (nc->client); + if (GNUNET_SYSERR == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Databse backend error: `%s'", emsg); + GNUNET_free (emsg); + } + if (GNUNET_SYSERR != ret) + { + nc->in_transaction = GNUNET_NO; + while (NULL != sa) + { + if ((nc != sa->nc) || + (GNUNET_NO == sa->uncommited)) + { + sa = sa->next; + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Discarding uncommited StoreActivity\n"); + sn = sa->next; + free_store_activity (sa); + sa = sn; + } + } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unknown control type %u\n", ntohs (tx_msg->control)); + GNUNET_break (0); + } +} + +/** + * Context for record remove operations passed from #handle_zone_to_name to + * #handle_zone_to_name_it as closure + */ +struct ZoneToNameCtx +{ + /** + * Namestore client + */ + struct NamestoreClient *nc; + + /** + * Request id (to be used in the response to the client). + */ + uint32_t rid; + + /** + * Set to #GNUNET_OK on success, #GNUNET_SYSERR on error. Note that + * not finding a name for the zone still counts as a 'success' here, + * as this field is about the success of executing the IPC protocol. + */ + enum GNUNET_ErrorCode ec; +}; + + +/** + * Zone to name iterator + * + * @param cls struct ZoneToNameCtx * + * @param seq sequence number of the record, MUST NOT BE ZERO + * @param zone_key the zone key + * @param name name + * @param rd_count number of records in @a rd + * @param rd record data + */ +static void +handle_zone_to_name_it (void *cls, + uint64_t seq, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct ZoneToNameCtx *ztn_ctx = cls; + struct GNUNET_MQ_Envelope *env; + struct ZoneToNameResponseMessage *ztnr_msg; + size_t name_len; + size_t key_len; + ssize_t rd_ser_len; + size_t msg_size; + char *name_tmp; + char *rd_tmp; + + GNUNET_assert (0 != seq); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found result for zone-to-name lookup: `%s'\n", + name); + ztn_ctx->ec = GNUNET_EC_NONE; + name_len = (NULL == name) ? 0 : strlen (name) + 1; + rd_ser_len = GNUNET_GNSRECORD_records_get_size (rd_count, rd); + if (rd_ser_len < 0) + { + GNUNET_break (0); + ztn_ctx->ec = htonl (GNUNET_EC_NAMESTORE_UNKNOWN); + return; + } + key_len = GNUNET_CRYPTO_private_key_get_length (zone_key); + msg_size = sizeof(struct ZoneToNameResponseMessage) + + name_len + rd_ser_len + key_len; + if (msg_size >= GNUNET_MAX_MESSAGE_SIZE) + { + GNUNET_break (0); + ztn_ctx->ec = GNUNET_EC_NAMESTORE_RECORD_TOO_BIG; + return; + } + env = + GNUNET_MQ_msg_extra (ztnr_msg, + key_len + name_len + rd_ser_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE); + ztnr_msg->gns_header.header.size = htons (msg_size); + ztnr_msg->gns_header.r_id = htonl (ztn_ctx->rid); + ztnr_msg->ec = htonl (ztn_ctx->ec); + ztnr_msg->rd_len = htons (rd_ser_len); + ztnr_msg->rd_count = htons (rd_count); + ztnr_msg->name_len = htons (name_len); + ztnr_msg->key_len = htons (key_len); + GNUNET_CRYPTO_write_private_key_to_buffer (zone_key, + &ztnr_msg[1], + key_len); + name_tmp = (char *) &ztnr_msg[1] + key_len; + GNUNET_memcpy (name_tmp, name, name_len); + rd_tmp = &name_tmp[name_len]; + GNUNET_assert ( + rd_ser_len == + GNUNET_GNSRECORD_records_serialize (rd_count, rd, rd_ser_len, rd_tmp)); + ztn_ctx->ec = GNUNET_EC_NONE; + GNUNET_MQ_send (ztn_ctx->nc->mq, env); +} + +static enum GNUNET_GenericReturnValue +check_zone_to_name (void *cls, + const struct ZoneToNameMessage *zis_msg) +{ + return GNUNET_OK; +} + + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME message + * + * @param cls client client sending the message + * @param ztn_msg message of type 'struct ZoneToNameMessage' + */ +static void +handle_zone_to_name (void *cls, const struct ZoneToNameMessage *ztn_msg) +{ + struct GNUNET_CRYPTO_PrivateKey zone; + struct GNUNET_CRYPTO_PublicKey value_zone; + struct NamestoreClient *nc = cls; + struct ZoneToNameCtx ztn_ctx; + struct GNUNET_MQ_Envelope *env; + struct ZoneToNameResponseMessage *ztnr_msg; + size_t key_len; + size_t pkey_len; + size_t kb_read; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ZONE_TO_NAME message\n"); + ztn_ctx.rid = ntohl (ztn_msg->gns_header.r_id); + ztn_ctx.nc = nc; + ztn_ctx.ec = GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND; + key_len = ntohs (ztn_msg->key_len); + if ((GNUNET_SYSERR == + GNUNET_CRYPTO_read_private_key_from_buffer (&ztn_msg[1], + key_len, + &zone, + &kb_read)) || + (kb_read != key_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Error parsing private key.\n"); + GNUNET_SERVICE_client_drop (nc->client); + GNUNET_break (0); + return; + } + pkey_len = ntohs (ztn_msg->pkey_len); + if ((GNUNET_SYSERR == + GNUNET_CRYPTO_read_public_key_from_buffer ((char*) &ztn_msg[1] + + key_len, + pkey_len, + &value_zone, + &kb_read)) || + (kb_read != pkey_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Error parsing public key.\n"); + GNUNET_SERVICE_client_drop (nc->client); + GNUNET_break (0); + return; + } + if (GNUNET_SYSERR == nc->GSN_database->zone_to_name (nc->GSN_database->cls, + &zone, + &value_zone, + &handle_zone_to_name_it, + &ztn_ctx)) + { + /* internal error, hang up instead of signalling something + that might be wrong */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + if (GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND == ztn_ctx.ec) + { + /* no result found, send empty response */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found no result for zone-to-name lookup.\n"); + env = GNUNET_MQ_msg (ztnr_msg, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE); + ztnr_msg->gns_header.r_id = ztn_msg->gns_header.r_id; + ztnr_msg->ec = htonl (GNUNET_EC_NAMESTORE_ZONE_NOT_FOUND); + GNUNET_MQ_send (nc->mq, env); + } + GNUNET_SERVICE_client_continue (nc->client); +} + + +/** + * Context for record remove operations passed from + * #run_zone_iteration_round to #zone_iterate_proc as closure + */ +struct ZoneIterationProcResult +{ + /** + * The zone iteration handle + */ + struct ZoneIteration *zi; + + /** + * Number of results left to be returned in this iteration. + */ + uint64_t limit; + + /** + * Skip a result and run again unless GNUNET_NO + */ + int run_again; +}; + + +/** + * Process results for zone iteration from database + * + * @param cls struct ZoneIterationProcResult + * @param seq sequence number of the record, MUST NOT BE ZERO + * @param zone_key the zone key + * @param name name + * @param rd_count number of records for this name + * @param rd record data + */ +static void +zone_iterate_proc (void *cls, + uint64_t seq, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct ZoneIterationProcResult *proc = cls; + + GNUNET_assert (0 != seq); + if ((NULL == zone_key) && (NULL == name)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration done\n"); + return; + } + if ((NULL == zone_key) || (NULL == name)) + { + /* what is this!? should never happen */ + GNUNET_break (0); + return; + } + if (0 == proc->limit) + { + /* what is this!? should never happen */ + GNUNET_break (0); + return; + } + proc->zi->seq = seq; + if (0 < send_lookup_response_with_filter (proc->zi->nc, + proc->zi->request_id, + zone_key, + name, + rd_count, + rd, + proc->zi->filter)) + proc->limit--; + else + proc->run_again = GNUNET_YES; +} + + +/** + * Perform the next round of the zone iteration. + * + * @param zi zone iterator to process + * @param limit number of results to return in one pass + */ +static void +run_zone_iteration_round (struct ZoneIteration *zi, uint64_t limit) +{ + struct ZoneIterationProcResult proc; + struct GNUNET_TIME_Absolute start; + struct GNUNET_TIME_Relative duration; + struct NamestoreClient *nc = zi->nc; + + memset (&proc, 0, sizeof(proc)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Asked to return up to %llu records at position %llu\n", + (unsigned long long) limit, + (unsigned long long) zi->seq); + proc.zi = zi; + proc.limit = limit; + proc.run_again = GNUNET_YES; + start = GNUNET_TIME_absolute_get (); + while (GNUNET_YES == proc.run_again) + { + proc.run_again = GNUNET_NO; + GNUNET_break (GNUNET_SYSERR != + nc->GSN_database->iterate_records (nc->GSN_database->cls, + (GNUNET_YES == + GNUNET_is_zero ( + &zi->zone)) + ? NULL + : &zi->zone, + zi->seq, + proc.limit, + &zone_iterate_proc, + &proc)); + } + duration = GNUNET_TIME_absolute_get_duration (start); + duration = GNUNET_TIME_relative_divide (duration, limit - proc.limit); + GNUNET_STATISTICS_set (statistics, + "NAMESTORE iteration delay (μs/record)", + duration.rel_value_us, + GNUNET_NO); + if (0 == proc.limit) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Returned %llu results, more results available\n", + (unsigned long long) limit); + zi->send_end = (0 != proc.limit); + if (0 == zi->cache_ops) + zone_iteration_done_client_continue (zi); +} + +static enum GNUNET_GenericReturnValue +check_iteration_start (void *cls, + const struct ZoneIterationStartMessage *zis_msg) +{ + uint16_t size; + size_t key_len; + + size = ntohs (zis_msg->gns_header.header.size); + key_len = ntohs (zis_msg->key_len); + + if (size < key_len + sizeof(*zis_msg)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START message + * + * @param cls the client sending the message + * @param zis_msg message from the client + */ +static void +handle_iteration_start (void *cls, + const struct ZoneIterationStartMessage *zis_msg) +{ + struct GNUNET_CRYPTO_PrivateKey zone; + struct NamestoreClient *nc = cls; + struct ZoneIteration *zi; + size_t key_len; + size_t kb_read; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ZONE_ITERATION_START message\n"); + key_len = ntohs (zis_msg->key_len); + zi = GNUNET_new (struct ZoneIteration); + if (0 < key_len) + { + if ((GNUNET_SYSERR == + GNUNET_CRYPTO_read_private_key_from_buffer (&zis_msg[1], + key_len, + &zone, + &kb_read)) || + (kb_read != key_len)) + { + GNUNET_SERVICE_client_drop (nc->client); + GNUNET_free (zi); + return; + } + zi->zone = zone; + } + zi->request_id = ntohl (zis_msg->gns_header.r_id); + zi->filter = ntohs (zis_msg->filter); + zi->offset = 0; + zi->nc = nc; + GNUNET_CONTAINER_DLL_insert (nc->op_head, nc->op_tail, zi); + run_zone_iteration_round (zi, 1); +} + + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP message + * + * @param cls the client sending the message + * @param zis_msg message from the client + */ +static void +handle_iteration_stop (void *cls, + const struct ZoneIterationStopMessage *zis_msg) +{ + struct NamestoreClient *nc = cls; + struct ZoneIteration *zi; + uint32_t rid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ZONE_ITERATION_STOP message\n"); + rid = ntohl (zis_msg->gns_header.r_id); + for (zi = nc->op_head; NULL != zi; zi = zi->next) + if (zi->request_id == rid) + break; + if (NULL == zi) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + GNUNET_CONTAINER_DLL_remove (nc->op_head, nc->op_tail, zi); + GNUNET_free (zi); + GNUNET_SERVICE_client_continue (nc->client); +} + + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT message + * + * @param cls the client sending the message + * @param zis_msg message from the client + */ +static void +handle_iteration_next (void *cls, + const struct ZoneIterationNextMessage *zis_msg) +{ + struct NamestoreClient *nc = cls; + struct ZoneIteration *zi; + uint32_t rid; + uint64_t limit; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ZONE_ITERATION_NEXT message\n"); + GNUNET_STATISTICS_update (statistics, + "Iteration NEXT messages received", + 1, + GNUNET_NO); + rid = ntohl (zis_msg->gns_header.r_id); + limit = GNUNET_ntohll (zis_msg->limit); + for (zi = nc->op_head; NULL != zi; zi = zi->next) + if (zi->request_id == rid) + break; + if (NULL == zi) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + run_zone_iteration_round (zi, limit); +} + + +/** + * Function called when the monitor is ready for more data, and we + * should thus unblock PUT operations that were blocked on the + * monitor not being ready. + */ +static void +monitor_unblock (struct ZoneMonitor *zm) +{ + struct StoreActivity *sa = sa_head; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Unblocking zone monitor %p\n", zm); + while ((NULL != sa) && (zm->limit > zm->iteration_cnt)) + { + struct StoreActivity *sn = sa->next; + + if (sa->zm_pos == zm) + continue_store_activity (sa, GNUNET_YES); + sa = sn; + } + if (zm->limit > zm->iteration_cnt) + { + zm->sa_waiting = GNUNET_NO; + if (NULL != zm->sa_wait_warning) + { + GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); + zm->sa_wait_warning = NULL; + } + } + else if (GNUNET_YES == zm->sa_waiting) + { + zm->sa_waiting_start = GNUNET_TIME_absolute_get (); + if (NULL != zm->sa_wait_warning) + GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); + zm->sa_wait_warning = + GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, + &warn_monitor_slow, + zm); + } +} + + +/** + * Send 'sync' message to zone monitor, we're now in sync. + * + * @param zm monitor that is now in sync + */ +static void +monitor_sync (struct ZoneMonitor *zm) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *sync; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Synching zone monitor %p\n", zm); + + env = GNUNET_MQ_msg (sync, GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_SYNC); + GNUNET_MQ_send (zm->nc->mq, env); + /* mark iteration done */ + zm->in_first_iteration = GNUNET_NO; + zm->iteration_cnt = 0; + if ((zm->limit > 0) && (zm->sa_waiting)) + monitor_unblock (zm); +} + + +/** + * Obtain the next datum during the zone monitor's zone initial iteration. + * + * @param cls zone monitor that does its initial iteration + */ +static void +monitor_iteration_next (void *cls); + + +/** + * A #GNUNET_NAMESTORE_RecordIterator for monitors. + * + * @param cls a 'struct ZoneMonitor *' with information about the monitor + * @param seq sequence number of the record, MUST NOT BE ZERO + * @param zone_key zone key of the zone + * @param name name + * @param rd_count number of records in @a rd + * @param rd array of records + */ +static void +monitor_iterate_cb (void *cls, + uint64_t seq, + const struct GNUNET_CRYPTO_PrivateKey *zone_key, + const char *name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct ZoneMonitor *zm = cls; + + GNUNET_assert (0 != seq); + zm->seq = seq; + GNUNET_assert (NULL != name); + GNUNET_STATISTICS_update (statistics, + "Monitor notifications sent", + 1, + GNUNET_NO); + if (0 < send_lookup_response_with_filter (zm->nc, 0, zone_key, name, + rd_count, rd, zm->filter)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sent records.\n"); + zm->limit--; + zm->iteration_cnt--; + } + else + zm->run_again = GNUNET_YES; + if ((0 == zm->iteration_cnt) && (0 != zm->limit)) + { + /* We are done with the current iteration batch, AND the + client would right now accept more, so go again! */ + GNUNET_assert (NULL == zm->task); + zm->task = GNUNET_SCHEDULER_add_now (&monitor_iteration_next, zm); + } +} + +static enum GNUNET_GenericReturnValue +check_monitor_start (void *cls, + const struct ZoneMonitorStartMessage *zis_msg) +{ + uint16_t size; + size_t key_len; + + size = ntohs (zis_msg->header.size); + key_len = ntohs (zis_msg->key_len); + + if (size < key_len + sizeof(*zis_msg)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START message + * + * @param cls the client sending the message + * @param zis_msg message from the client + */ +static void +handle_monitor_start (void *cls, const struct + ZoneMonitorStartMessage *zis_msg) +{ + struct GNUNET_CRYPTO_PrivateKey zone; + struct NamestoreClient *nc = cls; + struct ZoneMonitor *zm; + size_t key_len; + size_t kb_read; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ZONE_MONITOR_START message\n"); + zm = GNUNET_new (struct ZoneMonitor); + zm->nc = nc; + key_len = ntohs (zis_msg->key_len); + if (0 < key_len) + { + if ((GNUNET_SYSERR == + GNUNET_CRYPTO_read_private_key_from_buffer (&zis_msg[1], + key_len, + &zone, + &kb_read)) || + (kb_read != key_len)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error reading private key\n"); + GNUNET_SERVICE_client_drop (nc->client); + GNUNET_free (zm); + return; + } + zm->zone = zone; + } + zm->limit = 1; + zm->filter = ntohs (zis_msg->filter); + zm->in_first_iteration = (GNUNET_YES == ntohl (zis_msg->iterate_first)); + GNUNET_CONTAINER_DLL_insert (monitor_head, monitor_tail, zm); + GNUNET_SERVICE_client_mark_monitor (nc->client); + GNUNET_SERVICE_client_continue (nc->client); + GNUNET_notification_context_add (monitor_nc, nc->mq); + if (zm->in_first_iteration) + zm->task = GNUNET_SCHEDULER_add_now (&monitor_iteration_next, zm); + else + monitor_sync (zm); +} + + +/** + * Obtain the next datum during the zone monitor's zone initial iteration. + * + * @param cls zone monitor that does its initial iteration + */ +static void +monitor_iteration_next (void *cls) +{ + struct ZoneMonitor *zm = cls; + struct NamestoreClient *nc = zm->nc; + int ret; + + zm->task = NULL; + GNUNET_assert (0 == zm->iteration_cnt); + if (zm->limit > 16) + zm->iteration_cnt = zm->limit / 2; /* leave half for monitor events */ + else + zm->iteration_cnt = zm->limit; /* use it all */ + zm->run_again = GNUNET_YES; + while (GNUNET_YES == zm->run_again) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Running iteration\n"); + zm->run_again = GNUNET_NO; + ret = nc->GSN_database->iterate_records (nc->GSN_database->cls, + (GNUNET_YES == GNUNET_is_zero ( + &zm->zone)) ? NULL : &zm->zone, + zm->seq, + zm->iteration_cnt, + &monitor_iterate_cb, + zm); + } + if (GNUNET_SYSERR == ret) + { + GNUNET_SERVICE_client_drop (zm->nc->client); + return; + } + if (GNUNET_NO == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Zone empty... syncing\n"); + /* empty zone */ + monitor_sync (zm); + return; + } +} + +/** + * Handles a #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT message + * + * @param cls the client sending the message + * @param nm message from the client + */ +static void +handle_monitor_next (void *cls, const struct ZoneMonitorNextMessage *nm) +{ + struct NamestoreClient *nc = cls; + struct ZoneMonitor *zm; + uint64_t inc; + + inc = GNUNET_ntohll (nm->limit); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ZONE_MONITOR_NEXT message with limit %llu\n", + (unsigned long long) inc); + for (zm = monitor_head; NULL != zm; zm = zm->next) + if (zm->nc == nc) + break; + if (NULL == zm) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + GNUNET_SERVICE_client_continue (nc->client); + if (zm->limit + inc < zm->limit) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (nc->client); + return; + } + zm->limit += inc; + if ((zm->in_first_iteration) && (zm->limit == inc)) + { + /* We are still iterating, and the previous iteration must + have stopped due to the client's limit, so continue it! */ + GNUNET_assert (NULL == zm->task); + zm->task = GNUNET_SCHEDULER_add_now (&monitor_iteration_next, zm); + } + GNUNET_assert (zm->iteration_cnt <= zm->limit); + if ((zm->limit > zm->iteration_cnt) && (zm->sa_waiting)) + { + monitor_unblock (zm); + } + else if (GNUNET_YES == zm->sa_waiting) + { + if (NULL != zm->sa_wait_warning) + GNUNET_SCHEDULER_cancel (zm->sa_wait_warning); + zm->sa_waiting_start = GNUNET_TIME_absolute_get (); + zm->sa_wait_warning = + GNUNET_SCHEDULER_add_delayed (MONITOR_STALL_WARN_DELAY, + &warn_monitor_slow, + zm); + } +} + +/** + * Process namestore requests. + * + * @param cls closure + * @param cfg configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SERVICE_Handle *service) +{ + char *database; + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting namestore service\n"); + return_orphaned = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "namestore", + "RETURN_ORPHANED"); + GSN_cfg = cfg; + monitor_nc = GNUNET_notification_context_create (1); + statistics = GNUNET_STATISTICS_create ("namestore", cfg); + /* Loading database plugin */ + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, + "namestore", + "database", + &database)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No database backend configured\n"); + GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); + return; + } + GNUNET_asprintf (&db_lib_name, "libgnunet_plugin_namestore_%s", database); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Loading %s\n", db_lib_name); + GSN_database = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg); + GNUNET_free (database); + if (NULL == GSN_database) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not load database backend `%s'\n", + db_lib_name); + GNUNET_free (db_lib_name); + GNUNET_SCHEDULER_add_now (&cleanup_task, NULL); + return; + } + GNUNET_SCHEDULER_add_shutdown (&cleanup_task, NULL); +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN ( + "namestore", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (tx_control, + GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL, + struct TxControlMessage, + NULL), + GNUNET_MQ_hd_var_size (record_store, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE, + struct RecordStoreMessage, + NULL), + GNUNET_MQ_hd_var_size (record_lookup, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP, + struct LabelLookupMessage, + NULL), + GNUNET_MQ_hd_var_size (zone_to_name, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME, + struct ZoneToNameMessage, + NULL), + GNUNET_MQ_hd_var_size (iteration_start, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START, + struct ZoneIterationStartMessage, + NULL), + GNUNET_MQ_hd_fixed_size (iteration_next, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT, + struct ZoneIterationNextMessage, + NULL), + GNUNET_MQ_hd_fixed_size (iteration_stop, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP, + struct ZoneIterationStopMessage, + NULL), + GNUNET_MQ_hd_var_size (monitor_start, + GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START, + struct ZoneMonitorStartMessage, + NULL), + GNUNET_MQ_hd_fixed_size (monitor_next, + GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT, + struct ZoneMonitorNextMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +/* end of gnunet-service-namestore.c */ diff --git a/src/service/namestore/meson.build b/src/service/namestore/meson.build new file mode 100644 index 000000000..fd7cfe553 --- /dev/null +++ b/src/service/namestore/meson.build @@ -0,0 +1,139 @@ +libgnunetnamestore_src = ['namestore_api.c', 'namestore_api_monitor.c'] +libgnunetpluginnamestore_sqlite_src = ['plugin_namestore_sqlite.c'] + +gnunetnamestore_src = ['gnunet-namestore.c'] +gnunetservicenamestore_src = ['gnunet-service-namestore.c'] + +configure_file(input : 'namestore.conf.in', + output : 'namestore.conf', + configuration : cdata, + install: true, + install_dir: pkgcfgdir) + + +if get_option('monolith') + foreach p : libgnunetnamestore_src + libgnunetpluginnamestore_sqlite_src + gnunetservicenamestore_src + gnunet_src += 'namestore/' + p + endforeach + subdir_done() +endif + +libgnunetnamestore = library('gnunetnamestore', + libgnunetnamestore_src, + soversion: '0', + version: '0.0.1', + dependencies: [libgnunetutil_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')) +libgnunetnamestore_dep = declare_dependency(link_with : libgnunetnamestore) +pkg.generate(libgnunetnamestore, url: 'https://www.gnunet.org', + description : 'Provides API for storing GNS records to a database') + +shared_module('gnunet_plugin_rest_namestore', + ['plugin_rest_namestore.c'], + dependencies: [libgnunetrest_dep, + libgnunetidentity_dep, + libgnunetgnsrecordjson_dep, + libgnunetgnsrecord_dep, + libgnunetnamestore_dep, + libgnunetjson_dep, + libgnunetutil_dep, + json_dep, + mhd_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir') / 'gnunet') + + +shared_module('gnunet_plugin_namestore_sqlite', + libgnunetpluginnamestore_sqlite_src, + dependencies: [libgnunetutil_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep, + libgnunetsq_dep, + libgnunetstatistics_dep, + sqlite_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') + +if pq_dep.found() + shared_module('gnunet_plugin_namestore_postgres', + ['plugin_namestore_postgres.c'], + dependencies: [libgnunetutil_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep, + libgnunetpq_dep, + libgnunetstatistics_dep, + pq_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet') +endif + +executable ('gnunet-namestore', + gnunetnamestore_src, + dependencies: [libgnunetnamestore_dep, + libgnunetutil_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('bindir')) +executable ('gnunet-namestore-dbtool', + ['gnunet-namestore-dbtool.c'], + dependencies: [libgnunetnamestore_dep, + libgnunetutil_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('bindir')) +executable ('gnunet-namestore-zonefile', + ['gnunet-namestore-zonefile.c'], + dependencies: [libgnunetnamestore_dep, + libgnunetutil_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('bindir')) +executable ('gnunet-zoneimport', + ['gnunet-zoneimport.c'], + dependencies: [libgnunetnamestore_dep, + libgnunetutil_dep, + libgnunetstatistics_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('bindir')) +executable ('gnunet-service-namestore', + gnunetservicenamestore_src, + dependencies: [libgnunetnamestore_dep, + libgnunetutil_dep, + libgnunetnamecache_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep, + libgnunetstatistics_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet'/'libexec') +executable ('gnunet-namestore-fcfsd', + ['gnunet-namestore-fcfsd.c'], + dependencies: [libgnunetnamestore_dep, + libgnunetutil_dep, + libgnunetnamecache_dep, + libgnunetgnsrecord_dep, + libgnunetidentity_dep, + mhd_dep, + json_dep, + libgnunetjson_dep, + libgnunetstatistics_dep], + include_directories: [incdir, configuration_inc], + install: true, + install_dir: get_option('libdir')/'gnunet'/'libexec') + diff --git a/src/service/namestore/namestore.conf.in b/src/service/namestore/namestore.conf.in new file mode 100644 index 000000000..d817f3f95 --- /dev/null +++ b/src/service/namestore/namestore.conf.in @@ -0,0 +1,47 @@ +[namestore] +START_ON_DEMAND = @START_ON_DEMAND@ +RUN_PER_USER = YES +UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-namestore.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +@UNIXONLY@ PORT = 2099 +HOSTNAME = localhost +BINARY = gnunet-service-namestore +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; + +# Which database should we use? +DATABASE = sqlite + +# Should we optimize publishing record by caching the mapping +# from zone private keys to zone public keys in memory? +# (Set to NO if totally paranoid about keeping private keys +# in RAM longer than necessary.) +CACHE_KEYS = YES + + +[namestore-sqlite] +INIT_ON_CONNECT = YES +FILENAME = $GNUNET_DATA_HOME/namestore/sqlite.db + +[namestore-postgres] +# How to connect to the database +CONFIG = postgres:///gnunet +# Use asynchronous commit (SET synchronous_commit TO OFF). +ASYNC_COMMIT = NO +INIT_ON_CONNECT = YES +SQL_DIR = ${DATADIR}/sql/ + +[uri] +gns = gnunet-namestore -e 1a -u + + +[fcfsd] +# Name of the fcfs registration service binary (for ARM) +BINARY = gnunet-namestore-fcfsd +START_ON_DEMAND = NO +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-fcfsd.sock +RELATIVE_RECORD_EXPIRATION = 7 d + +# On what port does the FCFS daemon listen for HTTP clients? +HTTPPORT = 18080 diff --git a/src/service/namestore/namestore.h b/src/service/namestore/namestore.h new file mode 100644 index 000000000..35d54d317 --- /dev/null +++ b/src/service/namestore/namestore.h @@ -0,0 +1,491 @@ +/* + This file is part of GNUnet. + Copyright (C) 2011-2013 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file namestore/namestore.h + * @brief common internal definitions for namestore service + * @author Matthias Wachs + * @author Christian Grothoff + */ +#ifndef NAMESTORE_H +#define NAMESTORE_H + +/** + * Maximum length of any name, including 0-termination. + */ +#define MAX_NAME_LEN 256 + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Generic namestore message with op id + */ +struct GNUNET_NAMESTORE_Header +{ + /** + * header.type will be GNUNET_MESSAGE_TYPE_NAMESTORE_* + * header.size will be message size + */ + struct GNUNET_MessageHeader header; + + /** + * Request ID in NBO + */ + uint32_t r_id GNUNET_PACKED; +}; + +struct RecordSet +{ + /** + * Name length + */ + uint16_t name_len GNUNET_PACKED; + + /** + * Length of serialized record data + */ + uint16_t rd_len GNUNET_PACKED; + + /** + * Number of records contained + */ + uint16_t rd_count GNUNET_PACKED; + + /** + * Reserved for alignment. + */ + uint16_t reserved GNUNET_PACKED; + + + /* followed by: + * name with length name_len + * serialized record data with rd_count records + */ +}; + +/** + * Store a record to the namestore (as authority). + */ +struct RecordStoreMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Number of record sets + */ + uint16_t rd_set_count; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /** + * Followed by the private zone key + * Followed by rd_set_count RecordSets + */ +}; + + +/** + * Response to a record storage request. + */ +struct RecordStoreResponseMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * GNUNET_ErrorCode + */ + uint32_t ec GNUNET_PACKED; + +}; + + +/** + * Lookup a label + */ +struct LabelLookupMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Length of the name + */ + uint16_t label_len GNUNET_PACKED; + + /** + * GNUNET_YES if this lookup corresponds to an edit request + */ + uint16_t is_edit_request GNUNET_PACKED; + + /** + * The record filter + */ + uint16_t filter; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /* followed by: + * the private zone key + * name with length name_len + */ +}; + + +/** + * Lookup a label + */ +struct LabelLookupResponseMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Name length + */ + uint16_t name_len GNUNET_PACKED; + + /** + * Length of serialized record data + */ + uint16_t rd_len GNUNET_PACKED; + + /** + * Number of records contained + */ + uint16_t rd_count GNUNET_PACKED; + + /** + * Was the label found in the database?? + * #GNUNET_YES or #GNUNET_NO + */ + int16_t found GNUNET_PACKED; + + /** + * Reserved (alignment) + */ + uint16_t reserved GNUNET_PACKED; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /* followed by: + * the private zone key + * name with length name_len + * serialized record data with rd_count records + */ +}; + + +/** + * Lookup a name for a zone hash + */ +struct ZoneToNameMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /** + * Length of the public value zone key + */ + uint16_t pkey_len GNUNET_PACKED; + + /** + * Followed by + * - the private zone key to look up in + * - the public key of the target zone + */ +}; + + +/** + * Respone for zone to name lookup + */ +struct ZoneToNameResponseMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * result in NBO: #GNUNET_EC_NONE on success, + * #GNUNET_EC_NAMESTORE_NO_RESULTS if there were no + * results. + * Other error messages on error. + */ + int32_t ec GNUNET_PACKED; + + /** + * Length of the name + */ + uint16_t name_len GNUNET_PACKED; + + /** + * Length of serialized record data + */ + uint16_t rd_len GNUNET_PACKED; + + /** + * Number of records contained + */ + uint16_t rd_count GNUNET_PACKED; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /* followed by: + * the private zone key + * name with length name_len + * serialized record data with rd_count records + */ +}; + + +/** + * Record is returned from the namestore (as authority). + */ +struct RecordResultMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Expiration time if the record result (if any). + * Takes TOMBSTONEs into account. + */ + struct GNUNET_TIME_AbsoluteNBO expire; + + /** + * Name length + */ + uint16_t name_len GNUNET_PACKED; + + /** + * Length of serialized record data + */ + uint16_t rd_len GNUNET_PACKED; + + /** + * Number of records contained + */ + uint16_t rd_count GNUNET_PACKED; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /* followed by: + * the private key of the authority + * name with length name_len + * serialized record data with rd_count records + */ +}; + +/** + * Send a transaction control message. + */ +struct TxControlMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * always zero (for alignment) + */ + uint16_t reserved GNUNET_PACKED; + + /** + * The type of control message to send + */ + uint16_t control GNUNET_PACKED; + +}; + +/** + * Result of a transaction control message. + */ +struct TxControlResultMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL_RESULT + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Of type GNUNET_ErrorCode + */ + uint32_t ec GNUNET_PACKED; + +}; + + + +/** + * Start monitoring a zone. + */ +struct ZoneMonitorStartMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START + */ + struct GNUNET_MessageHeader header; + + /** + * #GNUNET_YES to first iterate over all records, + * #GNUNET_NO to only monitor changes.o + */ + uint32_t iterate_first GNUNET_PACKED; + + /** + * Record set filter control flags. + * See GNUNET_NAMESTORE_Filter enum. + */ + uint16_t filter; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /** + * Followed by the private zone key. + */ +}; + + +/** + * Ask for next result of zone iteration for the given operation + */ +struct ZoneMonitorNextMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved; + + /** + * Number of records to return to the iterator in one shot + * (before #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_MONITOR_NEXT + * should be send again). In NBO. + */ + uint64_t limit; +}; + + +/** + * Start a zone iteration for the given zone + */ +struct ZoneIterationStartMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Record set filter control flags. + * See GNUNET_NAMESTORE_Filter enum. + */ + uint16_t filter; + + /** + * Length of the zone key + */ + uint16_t key_len GNUNET_PACKED; + + /** + * Followed by the private zone key (optional) + */ +}; + + +/** + * Ask for next result of zone iteration for the given operation + */ +struct ZoneIterationNextMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT + */ + struct GNUNET_NAMESTORE_Header gns_header; + + /** + * Number of records to return to the iterator in one shot + * (before #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT + * should be send again). In NBO. + */ + uint64_t limit; +}; + + +/** + * Stop zone iteration for the given operation + */ +struct ZoneIterationStopMessage +{ + /** + * Type will be #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP + */ + struct GNUNET_NAMESTORE_Header gns_header; +}; + + +GNUNET_NETWORK_STRUCT_END + + +/* end of namestore.h */ +#endif diff --git a/src/service/namestore/namestore_api.c b/src/service/namestore/namestore_api.c new file mode 100644 index 000000000..7a4438e30 --- /dev/null +++ b/src/service/namestore/namestore_api.c @@ -0,0 +1,1563 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010-2013, 2016 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file namestore/namestore_api.c + * @brief API to access the NAMESTORE service + * @author Martin Schanzenbach + * @author Matthias Wachs + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_error_codes.h" +#include "gnunet_util_lib.h" +#include "gnunet_constants.h" +#include "gnunet_arm_service.h" +#include "gnunet_signatures.h" +#include "gnunet_gns_service.h" +#include "gnunet_namestore_service.h" +#include "namestore.h" + + +#define LOG(kind, ...) GNUNET_log_from (kind, "namestore-api", __VA_ARGS__) + +/** + * We grant the namestore up to 1 minute of latency, if it is slower than + * that, store queries will fail. + */ +#define NAMESTORE_DELAY_TOLERANCE GNUNET_TIME_UNIT_MINUTES + +/** + * An QueueEntry used to store information for a pending + * NAMESTORE record operation + */ +struct GNUNET_NAMESTORE_QueueEntry +{ + /** + * Kept in a DLL. + */ + struct GNUNET_NAMESTORE_QueueEntry *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_NAMESTORE_QueueEntry *prev; + + /** + * Main handle to access the namestore. + */ + struct GNUNET_NAMESTORE_Handle *h; + + /** + * Continuation to call + */ + GNUNET_NAMESTORE_ContinuationWithStatus cont; + + /** + * Closure for @e cont. + */ + void *cont_cls; + + /** + * Function to call with the records we get back; or NULL. + */ + GNUNET_NAMESTORE_RecordMonitor proc; + + /** + * Function to call with the records we get back; or NULL. + */ + GNUNET_NAMESTORE_RecordSetMonitor proc2; + + /** + * Closure for @e proc. + */ + void *proc_cls; + + /** + * Function to call on errors. + */ + GNUNET_SCHEDULER_TaskCallback error_cb; + + /** + * Closure for @e error_cb. + */ + void *error_cb_cls; + + /** + * Envelope of the message to send to the service, if not yet + * sent. + */ + struct GNUNET_MQ_Envelope *env; + + /** + * Task scheduled to warn us if the namestore is way too slow. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * The operation id this zone iteration operation has + */ + uint32_t op_id; +}; + + +/** + * Handle for a zone iterator operation + */ +struct GNUNET_NAMESTORE_ZoneIterator +{ + /** + * Kept in a DLL. + */ + struct GNUNET_NAMESTORE_ZoneIterator *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_NAMESTORE_ZoneIterator *prev; + + /** + * Main handle to access the namestore. + */ + struct GNUNET_NAMESTORE_Handle *h; + + /** + * Function to call on completion. + */ + GNUNET_SCHEDULER_TaskCallback finish_cb; + + /** + * Closure for @e error_cb. + */ + void *finish_cb_cls; + + /** + * The continuation to call with the results + */ + GNUNET_NAMESTORE_RecordMonitor proc; + + /** + * The continuation to call with the results + */ + GNUNET_NAMESTORE_RecordSetMonitor proc2; + + /** + * Closure for @e proc. + */ + void *proc_cls; + + /** + * Function to call on errors. + */ + GNUNET_SCHEDULER_TaskCallback error_cb; + + /** + * Closure for @e error_cb. + */ + void *error_cb_cls; + + /** + * Envelope of the message to send to the service, if not yet + * sent. + */ + struct GNUNET_MQ_Envelope *env; + + /** + * Private key of the zone. + */ + struct GNUNET_CRYPTO_PrivateKey zone; + + /** + * The operation id this zone iteration operation has + */ + uint32_t op_id; +}; + + +/** + * Connection to the NAMESTORE service. + */ +struct GNUNET_NAMESTORE_Handle +{ + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Connection to the service (if available). + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Head of pending namestore queue entries + */ + struct GNUNET_NAMESTORE_QueueEntry *op_head; + + /** + * Tail of pending namestore queue entries + */ + struct GNUNET_NAMESTORE_QueueEntry *op_tail; + + /** + * Head of pending namestore zone iterator entries + */ + struct GNUNET_NAMESTORE_ZoneIterator *z_head; + + /** + * Tail of pending namestore zone iterator entries + */ + struct GNUNET_NAMESTORE_ZoneIterator *z_tail; + + /** + * Reconnect task + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * Delay introduced before we reconnect. + */ + struct GNUNET_TIME_Relative reconnect_delay; + + /** + * Should we reconnect to service due to some serious error? + */ + int reconnect; + + /** + * The last operation id used for a NAMESTORE operation + */ + uint32_t last_op_id_used; +}; + + +/** + * Disconnect from service and then reconnect. + * + * @param h our handle + */ +static void +force_reconnect (struct GNUNET_NAMESTORE_Handle *h); + + +/** + * Find the queue entry that matches the @a rid + * + * @param h namestore handle + * @param rid id to look up + * @return NULL if @a rid was not found + */ +static struct GNUNET_NAMESTORE_QueueEntry * +find_qe (struct GNUNET_NAMESTORE_Handle *h, uint32_t rid) +{ + struct GNUNET_NAMESTORE_QueueEntry *qe; + + for (qe = h->op_head; qe != NULL; qe = qe->next) + if (qe->op_id == rid) + return qe; + return NULL; +} + + +/** + * Find the zone iteration entry that matches the @a rid + * + * @param h namestore handle + * @param rid id to look up + * @return NULL if @a rid was not found + */ +static struct GNUNET_NAMESTORE_ZoneIterator * +find_zi (struct GNUNET_NAMESTORE_Handle *h, uint32_t rid) +{ + struct GNUNET_NAMESTORE_ZoneIterator *ze; + + for (ze = h->z_head; ze != NULL; ze = ze->next) + if (ze->op_id == rid) + return ze; + return NULL; +} + + +/** + * Free @a qe. + * + * @param qe entry to free + */ +static void +free_qe (struct GNUNET_NAMESTORE_QueueEntry *qe) +{ + struct GNUNET_NAMESTORE_Handle *h = qe->h; + + GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, qe); + if (NULL != qe->env) + GNUNET_MQ_discard (qe->env); + if (NULL != qe->timeout_task) + GNUNET_SCHEDULER_cancel (qe->timeout_task); + GNUNET_free (qe); +} + + +/** + * Free @a ze. + * + * @param ze entry to free + */ +static void +free_ze (struct GNUNET_NAMESTORE_ZoneIterator *ze) +{ + struct GNUNET_NAMESTORE_Handle *h = ze->h; + + GNUNET_CONTAINER_DLL_remove (h->z_head, h->z_tail, ze); + if (NULL != ze->env) + GNUNET_MQ_discard (ze->env); + GNUNET_free (ze); +} + + +/** + * Check that @a rd_buf of length @a rd_len contains + * @a rd_count records. + * + * @param rd_len length of @a rd_buf + * @param rd_buf buffer with serialized records + * @param rd_count number of records expected + * @return #GNUNET_OK if @a rd_buf is well-formed + */ +static int +check_rd (size_t rd_len, const void *rd_buf, unsigned int rd_count) +{ + struct GNUNET_GNSRECORD_Data rd[rd_count]; + + if (GNUNET_OK != + GNUNET_GNSRECORD_records_deserialize (rd_len, rd_buf, rd_count, rd)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE + * + * @param cls + * @param msg the message we received + */ +static void +handle_record_store_response (void *cls, + const struct RecordStoreResponseMessage *msg) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + struct GNUNET_NAMESTORE_QueueEntry *qe; + enum GNUNET_ErrorCode res; + + qe = find_qe (h, ntohl (msg->gns_header.r_id)); + res = ntohl (msg->ec); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received RECORD_STORE_RESPONSE with result %d\n", + res); + if (NULL == qe) + return; + if (NULL != qe->cont) + qe->cont (qe->cont_cls, res); + free_qe (qe); +} + + +/** + * Check validity of an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE + * + * @param cls + * @param msg the message we received + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +check_lookup_result (void *cls, const struct LabelLookupResponseMessage *msg) +{ + const char *name; + size_t exp_msg_len; + size_t msg_len; + size_t name_len; + size_t rd_len; + size_t key_len; + + (void) cls; + rd_len = ntohs (msg->rd_len); + msg_len = ntohs (msg->gns_header.header.size); + name_len = ntohs (msg->name_len); + key_len = ntohs (msg->key_len); + exp_msg_len = sizeof(*msg) + name_len + rd_len + key_len; + if (0 != ntohs (msg->reserved)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (msg_len != exp_msg_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + name = (const char *) &msg[1] + key_len; + if ((name_len > 0) && ('\0' != name[name_len - 1])) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_NO == ntohs (msg->found)) + { + if (0 != ntohs (msg->rd_count)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; + } + return check_rd (rd_len, &name[name_len], ntohs (msg->rd_count)); +} + + +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE + * + * @param cls + * @param msg the message we received + */ +static void +handle_lookup_result (void *cls, const struct LabelLookupResponseMessage *msg) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_CRYPTO_PrivateKey private_key; + const char *name; + const char *rd_tmp; + size_t name_len; + size_t rd_len; + size_t key_len; + size_t kbytes_read; + unsigned int rd_count; + int16_t found = (int16_t) ntohs (msg->found); + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received RECORD_LOOKUP_RESULT (found=%i)\n", + found); + qe = find_qe (h, ntohl (msg->gns_header.r_id)); + if (NULL == qe) + return; + rd_len = ntohs (msg->rd_len); + rd_count = ntohs (msg->rd_count); + name_len = ntohs (msg->name_len); + key_len = ntohs (msg->key_len); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CRYPTO_read_private_key_from_buffer (&msg[1], + key_len, + &private_key, + &kbytes_read)); + GNUNET_assert (kbytes_read == key_len); + name = (const char *) &msg[1] + key_len; + if (GNUNET_NO == found) + { + /* label was not in namestore */ + if (NULL != qe->proc) + qe->proc (qe->proc_cls, &private_key, name, 0, NULL); + free_qe (qe); + return; + } + if (GNUNET_SYSERR == found) + { + if (NULL != qe->error_cb) + qe->error_cb (qe->error_cb_cls); + free_qe (qe); + return; + } + + rd_tmp = &name[name_len]; + { + struct GNUNET_GNSRECORD_Data rd[rd_count]; + + GNUNET_assert ( + GNUNET_OK == + GNUNET_GNSRECORD_records_deserialize (rd_len, rd_tmp, rd_count, rd)); + if (0 == name_len) + name = NULL; + if (NULL != qe->proc) + qe->proc (qe->proc_cls, + &private_key, + name, + rd_count, + (rd_count > 0) ? rd : NULL); + } + free_qe (qe); +} + + +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT + * + * @param cls + * @param msg the message we received + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +check_record_result (void *cls, const struct RecordResultMessage *msg) +{ + const char *name; + size_t msg_len; + size_t name_len; + size_t rd_len; + size_t key_len; + + (void) cls; + rd_len = ntohs (msg->rd_len); + msg_len = ntohs (msg->gns_header.header.size); + key_len = ntohs (msg->key_len); + name_len = ntohs (msg->name_len); + if (msg_len != sizeof(struct RecordResultMessage) + key_len + name_len + + rd_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + name = (const char *) &msg[1] + key_len; + if ((0 == name_len) || ('\0' != name[name_len - 1])) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (0 == key_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return check_rd (rd_len, &name[name_len], ntohs (msg->rd_count)); +} + + +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT + * + * @param cls + * @param msg the message we received + */ +static void +handle_record_result (void *cls, const struct RecordResultMessage *msg) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_NAMESTORE_ZoneIterator *ze; + struct GNUNET_CRYPTO_PrivateKey private_key; + const char *name; + const char *rd_tmp; + size_t name_len; + size_t rd_len; + size_t key_len; + size_t kbytes_read; + unsigned int rd_count; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received RECORD_RESULT\n"); + rd_len = ntohs (msg->rd_len); + rd_count = ntohs (msg->rd_count); + name_len = ntohs (msg->name_len); + key_len = ntohs (msg->key_len); + ze = find_zi (h, ntohl (msg->gns_header.r_id)); + qe = find_qe (h, ntohl (msg->gns_header.r_id)); + if ((NULL == ze) && (NULL == qe)) + return; /* rid not found */ + if ((NULL != ze) && (NULL != qe)) + { + GNUNET_break (0); /* rid ambiguous */ + force_reconnect (h); + return; + } + name = (const char *) &msg[1] + key_len; + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CRYPTO_read_private_key_from_buffer (&msg[1], + key_len, + &private_key, + &kbytes_read)); + GNUNET_assert (kbytes_read == key_len); + rd_tmp = &name[name_len]; + { + struct GNUNET_GNSRECORD_Data rd[rd_count]; + + GNUNET_assert ( + GNUNET_OK == + GNUNET_GNSRECORD_records_deserialize (rd_len, rd_tmp, rd_count, rd)); + if (0 == name_len) + name = NULL; + if (NULL != qe) + { + if (NULL != qe->proc) + qe->proc (qe->proc_cls, + &private_key, + name, + rd_count, + (rd_count > 0) ? rd : NULL); + free_qe (qe); + return; + } + if (NULL != ze) + { + // Store them here because a callback could free ze + GNUNET_NAMESTORE_RecordMonitor proc; + GNUNET_NAMESTORE_RecordSetMonitor proc2; + void *proc_cls = ze->proc_cls; + proc = ze->proc; + proc2 = ze->proc2; + if (NULL != proc) + proc (proc_cls, &private_key, name, rd_count, rd); + if (NULL != proc2) + proc2 (proc_cls, &private_key, name, + rd_count, rd, GNUNET_TIME_absolute_ntoh (msg->expire)); + return; + } + } + GNUNET_assert (0); +} + + +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END + * + * @param cls + * @param msg the message we received + */ +static void +handle_record_result_end (void *cls, const struct GNUNET_NAMESTORE_Header *msg) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_NAMESTORE_ZoneIterator *ze; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received RECORD_RESULT_END\n"); + ze = find_zi (h, ntohl (msg->r_id)); + qe = find_qe (h, ntohl (msg->r_id)); + if ((NULL == ze) && (NULL == qe)) + return; /* rid not found */ + if ((NULL != ze) && (NULL != qe)) + { + GNUNET_break (0); /* rid ambiguous */ + force_reconnect (h); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Zone iteration completed!\n"); + if (NULL == ze) + { + GNUNET_break (0); + force_reconnect (h); + return; + } + if (NULL != ze->finish_cb) + ze->finish_cb (ze->finish_cb_cls); + free_ze (ze); +} + +static void +handle_tx_control_result (void *cls, + const struct TxControlResultMessage *msg) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + struct GNUNET_NAMESTORE_QueueEntry *qe; + enum GNUNET_ErrorCode res; + + qe = find_qe (h, ntohl (msg->gns_header.r_id)); + res = ntohs (msg->ec); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received TX_CONTROL_RESULT with result %d\n", + res); + if (NULL == qe) + return; + if (NULL != qe->cont) + qe->cont (qe->cont_cls, res); + free_qe (qe); +} + +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE. + * + * @param qe the respective entry in the message queue + * @param msg the message we received + * @return #GNUNET_OK on success, #GNUNET_SYSERR if message malformed + */ +static int +check_zone_to_name_response (void *cls, + const struct ZoneToNameResponseMessage *msg) +{ + size_t name_len; + size_t rd_ser_len; + size_t key_len; + const char *name_tmp; + + (void) cls; + if (GNUNET_EC_NONE != ntohl (msg->ec)) + return GNUNET_OK; + key_len = ntohs (msg->key_len); + name_len = ntohs (msg->name_len); + rd_ser_len = ntohs (msg->rd_len); + if (ntohs (msg->gns_header.header.size) != + sizeof(struct ZoneToNameResponseMessage) + key_len + name_len + + rd_ser_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + name_tmp = (const char *) &msg[1] + key_len; + if ((name_len > 0) && ('\0' != name_tmp[name_len - 1])) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return check_rd (rd_ser_len, &name_tmp[name_len], ntohs (msg->rd_count)); +} + + +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE. + * + * @param cls + * @param msg the message we received + */ +static void +handle_zone_to_name_response (void *cls, + const struct ZoneToNameResponseMessage *msg) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_CRYPTO_PrivateKey zone; + enum GNUNET_ErrorCode res; + size_t name_len; + size_t rd_ser_len; + unsigned int rd_count; + const char *name_tmp; + const char *rd_tmp; + size_t key_len; + size_t kbytes_read; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received ZONE_TO_NAME_RESPONSE\n"); + qe = find_qe (h, ntohl (msg->gns_header.r_id)); + if (NULL == qe) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Response queue already gone...\n"); + return; + } + res = ntohl (msg->ec); + key_len = ntohs (msg->key_len); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CRYPTO_read_private_key_from_buffer (&msg[1], + key_len, + &zone, + &kbytes_read)); + GNUNET_assert (kbytes_read == key_len); + switch (res) + { + break; + + case GNUNET_EC_NAMESTORE_NO_RESULTS: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Namestore has no result for zone to name mapping \n"); + if (NULL != qe->proc) + qe->proc (qe->proc_cls, &zone, NULL, 0, NULL); + free_qe (qe); + return; + + case GNUNET_EC_NONE: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Namestore has result for zone to name mapping \n"); + name_len = ntohs (msg->name_len); + rd_count = ntohs (msg->rd_count); + rd_ser_len = ntohs (msg->rd_len); + name_tmp = (const char *) &msg[1] + key_len; + rd_tmp = &name_tmp[name_len]; + { + struct GNUNET_GNSRECORD_Data rd[rd_count]; + + GNUNET_assert (GNUNET_OK == + GNUNET_GNSRECORD_records_deserialize (rd_ser_len, + rd_tmp, + rd_count, + rd)); + /* normal end, call continuation with result */ + if (NULL != qe->proc) + qe->proc (qe->proc_cls, &zone, name_tmp, rd_count, rd); + /* return is important here: break would call continuation with error! */ + free_qe (qe); + return; + } + + default: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "An error occurred during zone to name operation: %s\n", + GNUNET_ErrorCode_get_hint (res)); + break; + } + /* error case, call continuation with error */ + if (NULL != qe->error_cb) + qe->error_cb (qe->error_cb_cls); + free_qe (qe); +} + + +/** + * Generic error handler, called with the appropriate error code and + * the same closure specified at the creation of the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls closure with the `struct GNUNET_NAMESTORE_Handle *` + * @param error error code + */ +static void +mq_error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + + (void) error; + force_reconnect (h); +} + + +/** + * Reconnect to namestore service. + * + * @param h the handle to the NAMESTORE service + */ +static void +reconnect (struct GNUNET_NAMESTORE_Handle *h) +{ + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_fixed_size (record_store_response, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE_RESPONSE, + struct RecordStoreResponseMessage, + h), + GNUNET_MQ_hd_var_size (zone_to_name_response, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME_RESPONSE, + struct ZoneToNameResponseMessage, + h), + GNUNET_MQ_hd_var_size (record_result, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT, + struct RecordResultMessage, + h), + GNUNET_MQ_hd_fixed_size (record_result_end, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT_END, + struct GNUNET_NAMESTORE_Header, + h), + GNUNET_MQ_hd_var_size (lookup_result, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP_RESPONSE, + struct LabelLookupResponseMessage, + h), + GNUNET_MQ_hd_fixed_size (tx_control_result, + GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL_RESULT, + struct TxControlResultMessage, + h), + GNUNET_MQ_handler_end () }; + struct GNUNET_NAMESTORE_ZoneIterator *it; + struct GNUNET_NAMESTORE_QueueEntry *qe; + + GNUNET_assert (NULL == h->mq); + h->mq = + GNUNET_CLIENT_connect (h->cfg, "namestore", handlers, &mq_error_handler, h); + if (NULL == h->mq) + return; + /* re-transmit pending requests that waited for a reconnect... */ + for (it = h->z_head; NULL != it; it = it->next) + { + GNUNET_MQ_send (h->mq, it->env); + it->env = NULL; + } + for (qe = h->op_head; NULL != qe; qe = qe->next) + { + GNUNET_MQ_send (h->mq, qe->env); + qe->env = NULL; + } +} + + +/** + * Re-establish the connection to the service. + * + * @param cls handle to use to re-connect. + */ +static void +reconnect_task (void *cls) +{ + struct GNUNET_NAMESTORE_Handle *h = cls; + + h->reconnect_task = NULL; + reconnect (h); +} + + +/** + * Disconnect from service and then reconnect. + * + * @param h our handle + */ +static void +force_reconnect (struct GNUNET_NAMESTORE_Handle *h) +{ + struct GNUNET_NAMESTORE_ZoneIterator *ze; + struct GNUNET_NAMESTORE_QueueEntry *qe; + + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + while (NULL != (ze = h->z_head)) + { + if (NULL != ze->error_cb) + ze->error_cb (ze->error_cb_cls); + free_ze (ze); + } + while (NULL != (qe = h->op_head)) + { + if (NULL != qe->error_cb) + qe->error_cb (qe->error_cb_cls); + if (NULL != qe->cont) + qe->cont (qe->cont_cls, + GNUNET_EC_NAMESTORE_UNKNOWN); + free_qe (qe); + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reconnecting to namestore\n"); + h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); + h->reconnect_task = + GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect_task, h); +} + + +/** + * Get a fresh operation id to distinguish between namestore requests + * + * @param h the namestore handle + * @return next operation id to use + */ +static uint32_t +get_op_id (struct GNUNET_NAMESTORE_Handle *h) +{ + return h->last_op_id_used++; +} + + +/** + * Initialize the connection with the NAMESTORE service. + * + * @param cfg configuration to use + * @return handle to the GNS service, or NULL on error + */ +struct GNUNET_NAMESTORE_Handle * +GNUNET_NAMESTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_NAMESTORE_Handle *h; + + h = GNUNET_new (struct GNUNET_NAMESTORE_Handle); + h->cfg = cfg; + reconnect (h); + if (NULL == h->mq) + { + GNUNET_free (h); + return NULL; + } + return h; +} + + +/** + * Disconnect from the namestore service (and free associated + * resources). + * + * @param h handle to the namestore + */ +void +GNUNET_NAMESTORE_disconnect (struct GNUNET_NAMESTORE_Handle *h) +{ + struct GNUNET_NAMESTORE_QueueEntry *q; + struct GNUNET_NAMESTORE_ZoneIterator *z; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n"); + GNUNET_break (NULL == h->op_head); + while (NULL != (q = h->op_head)) + { + GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, q); + GNUNET_free (q); + } + GNUNET_break (NULL == h->z_head); + while (NULL != (z = h->z_head)) + { + GNUNET_CONTAINER_DLL_remove (h->z_head, h->z_tail, z); + GNUNET_free (z); + } + if (NULL != h->mq) + { + GNUNET_MQ_destroy (h->mq); + h->mq = NULL; + } + if (NULL != h->reconnect_task) + { + GNUNET_SCHEDULER_cancel (h->reconnect_task); + h->reconnect_task = NULL; + } + GNUNET_free (h); +} + + +/** + * Task launched to warn the user that the namestore is + * excessively slow and that a query was thus dropped. + * + * @param cls a `struct GNUNET_NAMESTORE_QueueEntry *` + */ +static void +warn_delay (void *cls) +{ + struct GNUNET_NAMESTORE_QueueEntry *qe = cls; + + qe->timeout_task = NULL; + LOG (GNUNET_ERROR_TYPE_WARNING, + "Did not receive response from namestore after %s!\n", + GNUNET_STRINGS_relative_time_to_string (NAMESTORE_DELAY_TOLERANCE, + GNUNET_YES)); + if (NULL != qe->cont) + { + qe->cont (qe->cont_cls, GNUNET_EC_NAMESTORE_UNKNOWN); + qe->cont = NULL; + } + GNUNET_NAMESTORE_cancel (qe); +} + + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_records_store ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *pkey, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd, + GNUNET_NAMESTORE_ContinuationWithStatus cont, + void *cont_cls) +{ + struct GNUNET_NAMESTORE_RecordInfo ri; + unsigned int rds_sent; + ri.a_label = label; + ri.a_rd_count = rd_count; + ri.a_rd = (struct GNUNET_GNSRECORD_Data *) rd; + return GNUNET_NAMESTORE_records_store2 (h, pkey, 1, &ri, &rds_sent, + cont, cont_cls); +} + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_records_store2 ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *pkey, + unsigned int rd_set_count, + const struct GNUNET_NAMESTORE_RecordInfo *record_info, + unsigned int *rds_sent, + GNUNET_NAMESTORE_ContinuationWithStatus cont, + void *cont_cls) +{ + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_MQ_Envelope *env; + const char *label; + unsigned int rd_count; + const struct GNUNET_GNSRECORD_Data *rd; + char *name_tmp; + char *rd_ser; + ssize_t rd_ser_len[rd_set_count]; + size_t name_len; + uint32_t rid; + struct RecordStoreMessage *msg; + struct RecordSet *rd_set; + ssize_t sret; + int i; + size_t rd_set_len = 0; + size_t key_len = 0; + size_t max_len; + key_len = GNUNET_CRYPTO_private_key_get_length (pkey); + max_len = UINT16_MAX - key_len - sizeof (struct RecordStoreMessage); + + *rds_sent = 0; + for (i = 0; i < rd_set_count; i++) + { + label = record_info[i].a_label; + rd_count = record_info[i].a_rd_count; + rd = record_info[i].a_rd; + name_len = strlen (label) + 1; + if (name_len > MAX_NAME_LEN) + { + GNUNET_break (0); + *rds_sent = 0; + return NULL; + } + rd_ser_len[i] = GNUNET_GNSRECORD_records_get_size (rd_count, rd); + if (rd_ser_len[i] < 0) + { + GNUNET_break (0); + *rds_sent = 0; + return NULL; + } + if (rd_ser_len[i] > max_len) + { + GNUNET_break (0); + *rds_sent = 0; + return NULL; + } + if ((rd_set_len + sizeof (struct RecordSet) + name_len + rd_ser_len[i]) > + max_len) + break; + rd_set_len += sizeof (struct RecordSet) + name_len + rd_ser_len[i]; + } + *rds_sent = i; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending %u of %u records!\n", *rds_sent, rd_set_count); + rid = get_op_id (h); + qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); + qe->h = h; + qe->cont = cont; + qe->cont_cls = cont_cls; + qe->op_id = rid; + GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); + /* setup msg */ + env = GNUNET_MQ_msg_extra (msg, + key_len + rd_set_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_STORE); + GNUNET_assert (NULL != msg); + GNUNET_assert (NULL != env); + msg->gns_header.r_id = htonl (rid); + msg->key_len = htons (key_len); + msg->rd_set_count = htons ((uint16_t) (*rds_sent)); + GNUNET_CRYPTO_write_private_key_to_buffer (pkey, + &msg[1], + key_len); + rd_set = (struct RecordSet*) (((char*) &msg[1]) + key_len); + for (int i = 0; i < *rds_sent; i++) + { + label = record_info[i].a_label; + rd = record_info[i].a_rd; + name_len = strlen (label) + 1; + rd_set->name_len = htons (name_len); + rd_set->rd_count = htons (record_info[i].a_rd_count); + rd_set->rd_len = htons (rd_ser_len[i]); + rd_set->reserved = ntohs (0); + name_tmp = (char *) &rd_set[1]; + GNUNET_memcpy (name_tmp, label, name_len); + rd_ser = &name_tmp[name_len]; + sret = GNUNET_GNSRECORD_records_serialize (record_info[i].a_rd_count, + rd, rd_ser_len[i], rd_ser); + if ((0 > sret) || (sret != rd_ser_len[i])) + { + GNUNET_break (0); + GNUNET_free (env); + return NULL; + } + // Point to next RecordSet + rd_set = (struct RecordSet*) &name_tmp[name_len + rd_ser_len[i]]; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending NAMESTORE_RECORD_STORE message for name %u record sets\n", + *rds_sent); + qe->timeout_task = + GNUNET_SCHEDULER_add_delayed (NAMESTORE_DELAY_TOLERANCE, &warn_delay, qe); + if (NULL == h->mq) + { + qe->env = env; + LOG (GNUNET_ERROR_TYPE_WARNING, + "Delaying NAMESTORE_RECORD_STORE message as namestore is not ready!\n"); + } + else + { + GNUNET_MQ_send (h->mq, env); + } + return qe; +} + + +static struct GNUNET_NAMESTORE_QueueEntry * +records_lookup ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *pkey, + const char *label, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordMonitor rm, + void *rm_cls, + int is_edit_request, + enum GNUNET_GNSRECORD_Filter filter) +{ + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_MQ_Envelope *env; + struct LabelLookupMessage *msg; + size_t label_len; + size_t key_len; + + if (1 == (label_len = strlen (label) + 1)) + { + GNUNET_break (0); + return NULL; + } + + qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); + qe->h = h; + qe->error_cb = error_cb; + qe->error_cb_cls = error_cb_cls; + qe->proc = rm; + qe->proc_cls = rm_cls; + qe->op_id = get_op_id (h); + GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); + + key_len = GNUNET_CRYPTO_private_key_get_length (pkey); + env = GNUNET_MQ_msg_extra (msg, + label_len + key_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_LOOKUP); + msg->gns_header.r_id = htonl (qe->op_id); + GNUNET_CRYPTO_write_private_key_to_buffer (pkey, + &msg[1], + key_len); + + msg->key_len = htons (key_len); + msg->is_edit_request = htons (is_edit_request); + msg->label_len = htons (label_len); + msg->filter = htons (filter); + GNUNET_memcpy (((char*) &msg[1]) + key_len, label, label_len); + if (NULL == h->mq) + qe->env = env; + else + GNUNET_MQ_send (h->mq, env); + return qe; +} + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_records_lookup ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *pkey, + const char *label, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordMonitor rm, + void *rm_cls) +{ + return records_lookup (h, pkey, label, + error_cb, error_cb_cls, + rm, rm_cls, GNUNET_NO, GNUNET_GNSRECORD_FILTER_NONE); + +} + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_records_lookup2 ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *pkey, + const char *label, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordMonitor rm, + void *rm_cls, + enum GNUNET_GNSRECORD_Filter filter) +{ + return records_lookup (h, pkey, label, + error_cb, error_cb_cls, + rm, rm_cls, GNUNET_NO, filter); + +} + + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_records_edit ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *pkey, + const char *label, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordMonitor rm, + void *rm_cls) +{ + return records_lookup (h, pkey, label, + error_cb, error_cb_cls, + rm, rm_cls, GNUNET_YES, GNUNET_GNSRECORD_FILTER_NONE); +} + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_zone_to_name ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *zone, + const struct GNUNET_CRYPTO_PublicKey *value_zone, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordMonitor proc, + void *proc_cls) +{ + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_MQ_Envelope *env; + struct ZoneToNameMessage *msg; + uint32_t rid; + size_t key_len; + ssize_t pkey_len; + + rid = get_op_id (h); + qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); + qe->h = h; + qe->error_cb = error_cb; + qe->error_cb_cls = error_cb_cls; + qe->proc = proc; + qe->proc_cls = proc_cls; + qe->op_id = rid; + GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); + + key_len = GNUNET_CRYPTO_private_key_get_length (zone); + pkey_len = GNUNET_CRYPTO_public_key_get_length (value_zone); + env = GNUNET_MQ_msg_extra (msg, key_len + pkey_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_TO_NAME); + msg->gns_header.r_id = htonl (rid); + msg->key_len = htons (key_len); + msg->pkey_len = htons (pkey_len); + GNUNET_CRYPTO_write_private_key_to_buffer (zone, &msg[1], key_len); + GNUNET_CRYPTO_write_public_key_to_buffer (value_zone, + (char*) &msg[1] + key_len, + pkey_len); + if (NULL == h->mq) + qe->env = env; + else + GNUNET_MQ_send (h->mq, env); + return qe; +} + + +struct GNUNET_NAMESTORE_ZoneIterator * +GNUNET_NAMESTORE_zone_iteration_start ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *zone, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordMonitor proc, + void *proc_cls, + GNUNET_SCHEDULER_TaskCallback finish_cb, + void *finish_cb_cls) +{ + struct GNUNET_NAMESTORE_ZoneIterator *it; + struct GNUNET_MQ_Envelope *env; + struct ZoneIterationStartMessage *msg; + uint32_t rid; + size_t key_len = 0; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending ZONE_ITERATION_START message\n"); + rid = get_op_id (h); + it = GNUNET_new (struct GNUNET_NAMESTORE_ZoneIterator); + it->h = h; + it->error_cb = error_cb; + it->error_cb_cls = error_cb_cls; + it->finish_cb = finish_cb; + it->finish_cb_cls = finish_cb_cls; + it->proc = proc; + it->proc_cls = proc_cls; + it->op_id = rid; + if (NULL != zone) + { + it->zone = *zone; + key_len = GNUNET_CRYPTO_private_key_get_length (zone); + } + GNUNET_CONTAINER_DLL_insert_tail (h->z_head, h->z_tail, it); + env = GNUNET_MQ_msg_extra (msg, + key_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START); + msg->gns_header.r_id = htonl (rid); + msg->key_len = htons (key_len); + if (NULL != zone) + GNUNET_CRYPTO_write_private_key_to_buffer (zone, &msg[1], key_len); + if (NULL == h->mq) + it->env = env; + else + GNUNET_MQ_send (h->mq, env); + return it; +} + +struct GNUNET_NAMESTORE_ZoneIterator * +GNUNET_NAMESTORE_zone_iteration_start2 ( + struct GNUNET_NAMESTORE_Handle *h, + const struct GNUNET_CRYPTO_PrivateKey *zone, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordSetMonitor proc, + void *proc_cls, + GNUNET_SCHEDULER_TaskCallback finish_cb, + void *finish_cb_cls, + enum GNUNET_GNSRECORD_Filter filter) +{ + struct GNUNET_NAMESTORE_ZoneIterator *it; + struct GNUNET_MQ_Envelope *env; + struct ZoneIterationStartMessage *msg; + uint32_t rid; + size_t key_len = 0; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Sending ZONE_ITERATION_START message\n"); + rid = get_op_id (h); + it = GNUNET_new (struct GNUNET_NAMESTORE_ZoneIterator); + it->h = h; + it->error_cb = error_cb; + it->error_cb_cls = error_cb_cls; + it->finish_cb = finish_cb; + it->finish_cb_cls = finish_cb_cls; + it->proc2 = proc; + it->proc_cls = proc_cls; + it->op_id = rid; + if (NULL != zone) + { + it->zone = *zone; + key_len = GNUNET_CRYPTO_private_key_get_length (zone); + } + GNUNET_CONTAINER_DLL_insert_tail (h->z_head, h->z_tail, it); + env = GNUNET_MQ_msg_extra (msg, + key_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_START); + msg->gns_header.r_id = htonl (rid); + msg->key_len = htons (key_len); + msg->filter = htons ((uint16_t) filter); + if (NULL != zone) + GNUNET_CRYPTO_write_private_key_to_buffer (zone, &msg[1], key_len); + if (NULL == h->mq) + it->env = env; + else + GNUNET_MQ_send (h->mq, env); + return it; +} + + +void +GNUNET_NAMESTORE_zone_iterator_next (struct GNUNET_NAMESTORE_ZoneIterator *it, + uint64_t limit) +{ + struct GNUNET_NAMESTORE_Handle *h = it->h; + struct ZoneIterationNextMessage *msg; + struct GNUNET_MQ_Envelope *env; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending ZONE_ITERATION_NEXT message with limit %llu\n", + (unsigned long long) limit); + env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_NEXT); + msg->gns_header.r_id = htonl (it->op_id); + msg->limit = GNUNET_htonll (limit); + GNUNET_MQ_send (h->mq, env); +} + + +/** + * Stops iteration and releases the namestore handle for further calls. + * + * @param it the iterator + */ +void +GNUNET_NAMESTORE_zone_iteration_stop (struct GNUNET_NAMESTORE_ZoneIterator *it) +{ + struct GNUNET_NAMESTORE_Handle *h = it->h; + struct GNUNET_MQ_Envelope *env; + struct ZoneIterationStopMessage *msg; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending ZONE_ITERATION_STOP message\n"); + if (NULL != h->mq) + { + env = + GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAMESTORE_ZONE_ITERATION_STOP); + msg->gns_header.r_id = htonl (it->op_id); + GNUNET_MQ_send (h->mq, env); + } + free_ze (it); +} + + +/** + * Cancel a namestore operation. The final callback from the + * operation must not have been done yet. + * + * @param qe operation to cancel + */ +void +GNUNET_NAMESTORE_cancel (struct GNUNET_NAMESTORE_QueueEntry *qe) +{ + free_qe (qe); +} + +/** + * New API draft. Experimental + */ + +static struct GNUNET_NAMESTORE_QueueEntry * +send_transaction_control_msg (struct GNUNET_NAMESTORE_Handle *h, + GNUNET_NAMESTORE_ContinuationWithStatus cont, + void *cont_cls, + enum GNUNET_NAMESTORE_TxControl ctrl) +{ + struct GNUNET_NAMESTORE_QueueEntry *qe; + struct GNUNET_MQ_Envelope *env; + struct TxControlMessage *msg; + uint32_t rid; + + rid = get_op_id (h); + qe = GNUNET_new (struct GNUNET_NAMESTORE_QueueEntry); + qe->h = h; + qe->cont = cont; + qe->cont_cls = cont_cls; + qe->op_id = rid; + GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, qe); + + env = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_NAMESTORE_TX_CONTROL); + msg->gns_header.r_id = htonl (rid); + msg->control = htons (ctrl); + if (NULL == h->mq) + qe->env = env; + else + GNUNET_MQ_send (h->mq, env); + return qe; + GNUNET_break (0); + return NULL; +} + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_transaction_begin (struct GNUNET_NAMESTORE_Handle *h, + GNUNET_NAMESTORE_ContinuationWithStatus cont, + void *cont_cls) +{ + return send_transaction_control_msg (h, cont, cont_cls, + GNUNET_NAMESTORE_TX_BEGIN); +} + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_transaction_commit (struct GNUNET_NAMESTORE_Handle *h, + GNUNET_NAMESTORE_ContinuationWithStatus + cont, + void *cont_cls) +{ + return send_transaction_control_msg (h, cont, cont_cls, + GNUNET_NAMESTORE_TX_COMMIT); +} + + +struct GNUNET_NAMESTORE_QueueEntry * +GNUNET_NAMESTORE_transaction_rollback (struct GNUNET_NAMESTORE_Handle *h, + GNUNET_NAMESTORE_ContinuationWithStatus + cont, + void *cont_cls) +{ + return send_transaction_control_msg (h, cont, cont_cls, + GNUNET_NAMESTORE_TX_ROLLBACK); +} + + +/* end of namestore_api.c */ diff --git a/src/service/namestore/namestore_api_monitor.c b/src/service/namestore/namestore_api_monitor.c new file mode 100644 index 000000000..ec4ba879b --- /dev/null +++ b/src/service/namestore/namestore_api_monitor.c @@ -0,0 +1,443 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013, 2016, 2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file namestore/namestore_api_monitor.c + * @brief API to monitor changes in the NAMESTORE + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_constants.h" +#include "gnunet_arm_service.h" +#include "gnunet_signatures.h" +#include "gnunet_namestore_service.h" +#include "namestore.h" + + +/** + * Handle for a monitoring activity. + */ +struct GNUNET_NAMESTORE_ZoneMonitor +{ + /** + * Configuration (to reconnect). + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Handle to namestore service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Function to call on errors. + */ + GNUNET_SCHEDULER_TaskCallback error_cb; + + /** + * Closure for @e error_cb. + */ + void *error_cb_cls; + + /** + * Function to call on events. + */ + GNUNET_NAMESTORE_RecordMonitor monitor; + + /** + * Function to call on events. + */ + GNUNET_NAMESTORE_RecordSetMonitor monitor2; + + /** + * Record set filter for this monitor + */ + enum GNUNET_GNSRECORD_Filter filter; + + /** + * Closure for @e monitor. + */ + void *monitor_cls; + + /** + * Function called when we've synchronized. + */ + GNUNET_SCHEDULER_TaskCallback sync_cb; + + /** + * Closure for @e sync_cb. + */ + void *sync_cb_cls; + + /** + * Monitored zone. + */ + struct GNUNET_CRYPTO_PrivateKey zone; + + /** + * Do we first iterate over all existing records? + */ + int iterate_first; + + /** + * Zone key length + */ + uint32_t key_len; +}; + + +/** + * Reconnect to the namestore service. + * + * @param zm monitor to reconnect + */ +static void +reconnect (struct GNUNET_NAMESTORE_ZoneMonitor *zm); + + +/** + * Handle SYNC message from the namestore service. + * + * @param cls the monitor + * @param msg the sync message + */ +static void +handle_sync (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; + + (void) cls; + (void) msg; + if (NULL != zm->sync_cb) + zm->sync_cb (zm->sync_cb_cls); +} + + +/** + * We've received a notification about a change to our zone. + * Check that it is well-formed. + * + * @param cls the zone monitor handle + * @param lrm the message from the service. + */ +static int +check_result (void *cls, const struct RecordResultMessage *lrm) +{ + struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; + size_t lrm_len; + size_t exp_lrm_len; + size_t name_len; + size_t rd_len; + unsigned rd_count; + const char *name_tmp; + const char *rd_ser_tmp; + size_t key_len; + + (void) zm; + key_len = ntohs (lrm->key_len); + (void) cls; + if (0 == key_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + lrm_len = ntohs (lrm->gns_header.header.size); + rd_len = ntohs (lrm->rd_len); + rd_count = ntohs (lrm->rd_count); + name_len = ntohs (lrm->name_len); + if (name_len > MAX_NAME_LEN) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + exp_lrm_len = sizeof(struct RecordResultMessage) + name_len + rd_len + key_len; + if (lrm_len != exp_lrm_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (0 == name_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + name_tmp = (const char *) &lrm[1] + key_len; + if (name_tmp[name_len - 1] != '\0') + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + rd_ser_tmp = (const char *) &name_tmp[name_len]; + { + struct GNUNET_GNSRECORD_Data rd[rd_count]; + + if (GNUNET_OK != + GNUNET_GNSRECORD_records_deserialize (rd_len, rd_ser_tmp, rd_count, rd)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; +} + + +/** + * We've received a notification about a change to our zone. + * Forward to monitor callback. + * + * @param cls the zone monitor handle + * @param lrm the message from the service. + */ +static void +handle_result (void *cls, const struct RecordResultMessage *lrm) +{ + struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; + struct GNUNET_CRYPTO_PrivateKey private_key; + size_t name_len; + size_t rd_len; + size_t key_len; + size_t kbytes_read; + unsigned rd_count; + const char *name_tmp; + const char *rd_ser_tmp; + + key_len = ntohs (lrm->key_len); + rd_len = ntohs (lrm->rd_len); + rd_count = ntohs (lrm->rd_count); + name_len = ntohs (lrm->name_len); + name_tmp = (const char *) &lrm[1] + key_len; + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CRYPTO_read_private_key_from_buffer (&lrm[1], + key_len, + &private_key, + &kbytes_read)); + GNUNET_assert (kbytes_read == key_len); + rd_ser_tmp = (const char *) &name_tmp[name_len]; + { + struct GNUNET_GNSRECORD_Data rd[rd_count]; + + GNUNET_assert ( + GNUNET_OK == + GNUNET_GNSRECORD_records_deserialize (rd_len, rd_ser_tmp, rd_count, rd)); + if (NULL != zm->monitor2) + zm->monitor2 (zm->monitor_cls, &private_key, name_tmp, + rd_count, rd, GNUNET_TIME_absolute_ntoh (lrm->expire)); + else + zm->monitor (zm->monitor_cls, &private_key, name_tmp, rd_count, rd); + } +} + + +/** + * Generic error handler, called with the appropriate error code and + * the same closure specified at the creation of the message queue. + * Not every message queue implementation supports an error handler. + * + * @param cls closure with the `struct GNUNET_NAMESTORE_ZoneMonitor *` + * @param error error code + */ +static void +mq_error_handler (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_NAMESTORE_ZoneMonitor *zm = cls; + + (void) error; + reconnect (zm); +} + + +/** + * Reconnect to the namestore service. + * + * @param zm monitor to reconnect + */ +static void +reconnect (struct GNUNET_NAMESTORE_ZoneMonitor *zm) +{ + struct GNUNET_MQ_MessageHandler handlers[] = + { GNUNET_MQ_hd_fixed_size (sync, + GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_SYNC, + struct GNUNET_MessageHeader, + zm), + GNUNET_MQ_hd_var_size (result, + GNUNET_MESSAGE_TYPE_NAMESTORE_RECORD_RESULT, + struct RecordResultMessage, + zm), + GNUNET_MQ_handler_end () }; + struct GNUNET_MQ_Envelope *env; + struct ZoneMonitorStartMessage *sm; + + if (NULL != zm->mq) + { + GNUNET_MQ_destroy (zm->mq); + zm->error_cb (zm->error_cb_cls); + } + zm->mq = GNUNET_CLIENT_connect (zm->cfg, + "namestore", + handlers, + &mq_error_handler, + zm); + if (NULL == zm->mq) + return; + env = GNUNET_MQ_msg_extra (sm, + zm->key_len, + GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_START); + sm->iterate_first = htonl (zm->iterate_first); + if (0 < zm->key_len) + GNUNET_CRYPTO_write_private_key_to_buffer (&zm->zone, + &sm[1], + zm->key_len); + sm->key_len = htons (zm->key_len); + sm->filter = htons (zm->filter); + GNUNET_MQ_send (zm->mq, env); +} + + +struct GNUNET_NAMESTORE_ZoneMonitor * +GNUNET_NAMESTORE_zone_monitor_start ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CRYPTO_PrivateKey *zone, + int iterate_first, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordMonitor monitor, + void *monitor_cls, + GNUNET_SCHEDULER_TaskCallback sync_cb, + void *sync_cb_cls) +{ + struct GNUNET_NAMESTORE_ZoneMonitor *zm; + + zm = GNUNET_new (struct GNUNET_NAMESTORE_ZoneMonitor); + if (NULL != zone) + { + zm->key_len = GNUNET_CRYPTO_private_key_get_length (zone); + zm->zone = *zone; + } + zm->iterate_first = iterate_first; + zm->error_cb = error_cb; + zm->error_cb_cls = error_cb_cls; + zm->monitor = monitor; + zm->monitor_cls = monitor_cls; + zm->sync_cb = sync_cb; + zm->sync_cb_cls = sync_cb_cls; + zm->cfg = cfg; + reconnect (zm); + if (NULL == zm->mq) + { + GNUNET_free (zm); + return NULL; + } + return zm; +} + +struct GNUNET_NAMESTORE_ZoneMonitor * +GNUNET_NAMESTORE_zone_monitor_start2 ( + const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CRYPTO_PrivateKey *zone, + int iterate_first, + GNUNET_SCHEDULER_TaskCallback error_cb, + void *error_cb_cls, + GNUNET_NAMESTORE_RecordSetMonitor monitor, + void *monitor_cls, + GNUNET_SCHEDULER_TaskCallback sync_cb, + void *sync_cb_cls, + enum GNUNET_GNSRECORD_Filter filter) +{ + struct GNUNET_NAMESTORE_ZoneMonitor *zm; + + zm = GNUNET_new (struct GNUNET_NAMESTORE_ZoneMonitor); + if (NULL != zone) + { + zm->key_len = GNUNET_CRYPTO_private_key_get_length (zone); + zm->zone = *zone; + } + zm->iterate_first = iterate_first; + zm->error_cb = error_cb; + zm->error_cb_cls = error_cb_cls; + zm->monitor2 = monitor; + zm->monitor_cls = monitor_cls; + zm->sync_cb = sync_cb; + zm->sync_cb_cls = sync_cb_cls; + zm->cfg = cfg; + zm->filter = filter; + reconnect (zm); + if (NULL == zm->mq) + { + GNUNET_free (zm); + return NULL; + } + return zm; +} + + +/** + * Calls the monitor processor specified in #GNUNET_NAMESTORE_zone_monitor_start + * for the next record(s). This function is used to allow clients that merely + * monitor the NAMESTORE to still throttle namestore operations, so we can be + * sure that the monitors can keep up. + * + * Note that #GNUNET_NAMESTORE_records_store() only waits for this + * call if the previous limit set by the client was already reached. + * Thus, by using a @a limit greater than 1, monitors basically enable + * a queue of notifications to be processed asynchronously with some + * delay. Note that even with a limit of 1 the + * #GNUNET_NAMESTORE_records_store() function will run asynchronously + * and the continuation may be invoked before the monitors completed + * (or even started) processing the notification. Thus, monitors will + * only closely track the current state of the namestore, but not + * be involved in the transactions. + * + * @param zm the monitor + * @param limit number of records to return to the iterator in one shot + * (before #GNUNET_NAMESTORE_zone_monitor_next is to be called again) + */ +void +GNUNET_NAMESTORE_zone_monitor_next (struct GNUNET_NAMESTORE_ZoneMonitor *zm, + uint64_t limit) +{ + struct GNUNET_MQ_Envelope *env; + struct ZoneMonitorNextMessage *nm; + + env = GNUNET_MQ_msg (nm, GNUNET_MESSAGE_TYPE_NAMESTORE_MONITOR_NEXT); + nm->limit = GNUNET_htonll (limit); + GNUNET_MQ_send (zm->mq, env); +} + + +/** + * Stop monitoring a zone for changes. + * + * @param zm handle to the monitor activity to stop + */ +void +GNUNET_NAMESTORE_zone_monitor_stop (struct GNUNET_NAMESTORE_ZoneMonitor *zm) +{ + if (NULL != zm->mq) + { + GNUNET_MQ_destroy (zm->mq); + zm->mq = NULL; + } + GNUNET_free (zm); +} + + +/* end of namestore_api_monitor.c */ diff --git a/src/zonemaster/Makefile.am b/src/zonemaster/Makefile.am index 8f4e74588..5a96afa83 100644 --- a/src/zonemaster/Makefile.am +++ b/src/zonemaster/Makefile.am @@ -26,7 +26,7 @@ gnunet_service_zonemaster_LDADD = \ $(top_builddir)/src/service/identity/libgnunetidentity.la \ $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ $(top_builddir)/src/lib/util/libgnunetutil.la \ - $(top_builddir)/src/namestore/libgnunetnamestore.la \ + $(top_builddir)/src/service/namestore/libgnunetnamestore.la \ $(top_builddir)/src/service/namecache/libgnunetnamecache.la \ $(GN_LIBINTL) \ -lpthread -- cgit v1.2.3