This file is part of GNUnet.
Copyright (C) 2012, 2013, 2014 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 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
- General Public License for more details.
+ 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 <http://www.gnu.org/licenses/>.
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA.
+ SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file gnunet-namestore.c
#include <gnunet_namestore_service.h>
+/**
+ * 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;
+
+};
+
+
/**
* Handle to the namestore.
*/
*/
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 *del_qe;
+/**
+ * Queue entry for the 'set/replace' operation.
+ */
+static struct GNUNET_NAMESTORE_QueueEntry *set_qe;
+
/**
* Name of the records to add/list/remove.
*/
static size_t data_size;
/**
- * Expirationstring converted to relative time.
+ * Expiration string converted to numeric value.
*/
-static struct GNUNET_TIME_Relative etime_rel;
-
-/**
- * Expirationstring converted to absolute time.
- */
-static struct GNUNET_TIME_Absolute etime_abs;
+static uint64_t etime;
/**
* Is expiration time relative or absolute time?
*/
static int monitor;
+/**
+ * Entry in record set for processing records in bulk.
+ */
+static struct RecordSetEntry *recordset;
+
/**
* Task run on shutdown. Cleans up everything.
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);
{
if ( (NULL == add_qe) &&
(NULL == add_qe_uri) &&
+ (NULL == get_qe) &&
(NULL == del_qe) &&
(NULL == reverse_qe) &&
(NULL == list_it) )
}
+/**
+ * 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_EcdsaPrivateKey *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 (rname,
+ rd_len,
+ rd);
+ test_finished ();
+}
+
+
/**
* Function called once we are in sync in monitor mode.
*
/**
- * Function called if lookup fails.
+ * 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");
+ test_finished ();
+}
+
+
+/**
+ * Function called if lookup fails.
+ */
+static void
+add_error_cb (void *cls)
{
(void) cls;
add_qe = NULL;
ret = 1;
test_finished ();
return;
- case GNUNET_GNSRECORD_TYPE_GNS2DNS:
- fprintf (stderr,
- _("A %s record exists already under `%s', no other records can be added.\n"),
- "GNS2DNS",
- rec_name);
- ret = 1;
- test_finished ();
- return;
}
}
switch (type)
}
break;
case GNUNET_GNSRECORD_TYPE_GNS2DNS:
- if (0 != rd_count)
- {
- fprintf (stderr,
- _("Records already exist under `%s', cannot add `%s' record.\n"),
- rec_name,
- "GNS2DNS");
- ret = 1;
- test_finished ();
- return;
- }
+ for (unsigned int i=0;i<rd_count;i++)
+ if (GNUNET_GNSRECORD_TYPE_GNS2DNS != rd[i].record_type)
+ {
+ fprintf (stderr,
+ _("Non-GNS2DNS records already exist under `%s', cannot add GNS2DNS record.\n"),
+ rec_name);
+ ret = 1;
+ test_finished ();
+ return;
+ }
break;
}
memset (rdn,
rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
if (1 != is_public)
rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
+ rde->expiration_time = etime;
if (GNUNET_YES == etime_is_rel)
- {
- rde->expiration_time = etime_rel.rel_value_us;
rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
- }
- else if (GNUNET_NO == etime_is_rel)
- rde->expiration_time = etime_abs.abs_value_us;
- else
+ 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,
reverse_qe = NULL;
if (NULL == label)
FPRINTF (stdout,
- "%s.zkey\n",
+ "%s\n",
reverse_pkey);
else
FPRINTF (stdout,
- "%s.gnu\n",
- label);
+ "%s.%s\n",
+ label,
+ ego_name);
test_finished ();
}
}
+/**
+ * Parse expiration time.
+ *
+ * @param expirationstring text to parse
+ * @param etime_is_rel[out] set to #GNUNET_YES if time is relative
+ * @param etime[out] 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;
+ 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;
+}
+
+
+/**
+ * Function called when namestore is done with the replace
+ * operation.
+ *
+ * @param cls NULL
+ * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
+ * #GNUNET_NO if content was already there or not found
+ * #GNUNET_YES (or other positive value) on success
+ * @param emsg NULL on success, otherwise an error message
+ */
+static void
+replace_cont (void *cls,
+ int success,
+ const char *emsg)
+{
+ (void) cls;
+
+ set_qe = NULL;
+ if (GNUNET_OK != success)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ _("Failed to replace records: %s\n"),
+ emsg);
+ ret = 1; /* fail from 'main' */
+ }
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
/**
* Callback invoked from identity service with ego information.
* An @a ego of NULL means the ego was not found.
GNUNET_free_non_null (ego_name);
ego_name = NULL;
- if (! (add|del|list|(NULL != nickstring)|(NULL != uri)|(NULL != reverse_pkey)) )
+ if (! (add|del|list|(NULL != nickstring)|(NULL != uri)|(NULL != reverse_pkey)|(NULL != recordset)) )
{
/* nothing more to be done */
fprintf (stderr,
return;
}
GNUNET_CRYPTO_ecdsa_key_get_public (&zone_pkey,
- &pub);
-
+ &pub);
ns = GNUNET_NAMESTORE_connect (cfg);
if (NULL == ns)
{
_("Failed to connect to namestore\n"));
return;
}
+
+ if (NULL != recordset)
+ {
+ /* replace entire record set */
+ unsigned int rd_count;
+ struct GNUNET_GNSRECORD_Data *rd;
+
+ if (NULL == name)
+ {
+ fprintf (stderr,
+ _("Missing option `%s' for operation `%s'\n"),
+ "-R", _("replace"));
+ GNUNET_SCHEDULER_shutdown ();
+ ret = 1;
+ 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 (add)
{
if (NULL == name)
ret = 1;
return;
}
- if (0 == strcmp (expirationstring,
- "never"))
- {
- etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
- etime_is_rel = GNUNET_NO;
- }
- else if (GNUNET_OK ==
- GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
- &etime_rel))
- {
- etime_is_rel = GNUNET_YES;
- 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));
- }
- else if (GNUNET_OK ==
- GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
- &etime_abs))
- {
- etime_is_rel = GNUNET_NO;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Storing record with absolute expiration time of %s\n",
- GNUNET_STRINGS_absolute_time_to_string (etime_abs));
- }
- else
+ if (GNUNET_OK !=
+ parse_expiration (expirationstring,
+ &etime_is_rel,
+ &etime))
{
fprintf (stderr,
_("Invalid time format `%s'\n"),
add_qe = GNUNET_NAMESTORE_records_lookup (ns,
&zone_pkey,
name,
- &lookup_error_cb,
+ &add_error_cb,
NULL,
&get_existing_record,
NULL);
}
if (list)
{
- list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
- &zone_pkey,
- &zone_iteration_error_cb,
- NULL,
- &display_record_iterator,
- NULL,
- &zone_iteration_finished,
- NULL);
+ if (NULL != name)
+ 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_start (ns,
+ &zone_pkey,
+ &zone_iteration_error_cb,
+ NULL,
+ &display_record_iterator,
+ NULL,
+ &zone_iteration_finished,
+ NULL);
}
if (NULL != reverse_pkey)
{
rd.data = &pkey;
rd.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
- if (GNUNET_YES == etime_is_rel)
- {
- rd.expiration_time = etime_rel.rel_value_us;
+ rd.expiration_time = etime;
+ if (GNUNET_YES == etime_is_rel)
rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
- }
- else if (GNUNET_NO == etime_is_rel)
- rd.expiration_time = etime_abs.abs_value_us;
- else
- rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
-
if (1 == is_shadow)
rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
(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]);
if ( (NULL != args[0]) &&
(NULL == uri) )
uri = GNUNET_strdup (args[0]);
}
+/**
+ * Command-line option parser function that allows the user to specify
+ * a complete record as one argument for adding/removing. A pointer
+ * to the head of the list of record sets must be passed as the "scls"
+ * argument.
+ *
+ * @param ctx command line processor context
+ * @param scls must be of type "struct GNUNET_FS_Uri **"
+ * @param option name of the option (typically 'R')
+ * @param value command line argument given; format is
+ * "TTL TYPE FLAGS VALUE" where TTL is an expiration time (rel or abs),
+ * always given in seconds (without the unit),
+ * TYPE is a DNS/GNS record type, FLAGS is either "n" for no flags or
+ * a combination of 's' (shadow) and 'p' (public) and VALUE is the
+ * value (in human-readable format)
+ * @return #GNUNET_OK on success
+ */
+static int
+multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
+ void *scls,
+ const char *option,
+ const char *value)
+{
+ struct RecordSetEntry **head = scls;
+ struct RecordSetEntry *r;
+ struct GNUNET_GNSRECORD_Data record;
+ char *cp;
+ char *tok;
+ char *saveptr;
+ int etime_is_rel;
+ void *raw_data;
+
+ (void) ctx;
+ (void) option;
+ cp = GNUNET_strdup (value);
+ tok = strtok_r (cp, " ", &saveptr);
+ if (NULL == tok)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Empty record line argument is not allowed.\n"));
+ GNUNET_free (cp);
+ return GNUNET_SYSERR;
+ }
+ {
+ char *etime_in_s;
+
+ GNUNET_asprintf (&etime_in_s,
+ "%s s",
+ tok);
+ if (GNUNET_OK !=
+ parse_expiration (etime_in_s,
+ &etime_is_rel,
+ &record.expiration_time))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Invalid expiration time `%s' (must be without unit)\n"),
+ tok);
+ GNUNET_free (cp);
+ GNUNET_free (etime_in_s);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (etime_in_s);
+ }
+ tok = strtok_r (NULL, " ", &saveptr);
+ if (NULL == tok)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Missing entries in record line `%s'.\n"),
+ value);
+ 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,
+ _("Missing entries in record line `%s'.\n"),
+ value);
+ GNUNET_free (cp);
+ return GNUNET_SYSERR;
+ }
+ record.flags = GNUNET_GNSRECORD_RF_NONE;
+ if (etime_is_rel)
+ 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_SHADOW_RECORD;
+ /* find beginning of record value */
+ tok = strchr (&value[tok - cp], (unsigned char) ' ');
+ if (NULL == tok)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Missing entries in record line `%s'.\n"),
+ value);
+ GNUNET_free (cp);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (cp);
+ tok++; /* skip space */
+ 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);
+ return GNUNET_SYSERR;
+ }
+
+ 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;
+}
+
+
+/**
+ * Allow user to specify keywords.
+ *
+ * @param shortName short name of the option
+ * @param name long name of the option
+ * @param argumentHelp help text for the option argument
+ * @param description long help text for the option
+ * @param[out] topKeywords set to the desired value
+ */
+struct GNUNET_GETOPT_CommandLineOption
+multirecord_option (char shortName,
+ const char *name,
+ const char *argumentHelp,
+ const char *description,
+ struct RecordSetEntry **rs)
+{
+ struct GNUNET_GETOPT_CommandLineOption clo = {
+ .shortName = shortName,
+ .name = name,
+ .argumentHelp = argumentHelp,
+ .description = description,
+ .require_argument = 1,
+ .processor = &multirecord_process,
+ .scls = (void *) rs
+ };
+
+ return clo;
+}
+
+
+
/**
* The main function for gnunet-namestore.
*
"PKEY",
gettext_noop ("determine our name for the given PKEY"),
&reverse_pkey),
+ multirecord_option ('R',
+ "replace",
+ "RECORDLINE",
+ gettext_noop ("set record set to values given by (possibly multiple) RECORDLINES; can be specified multiple times"),
+ &recordset),
GNUNET_GETOPT_option_string ('t',
"type",
"TYPE",