/*
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 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 .
*/
/**
* @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
/**
* Handle to the namestore.
*/
static struct GNUNET_NAMESTORE_Handle *ns;
/**
* Private key for the our zone.
*/
static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
/**
* Handle to identity lookup.
*/
static struct GNUNET_IDENTITY_EgoLookup *el;
/**
* Identity service handle
*/
static struct GNUNET_IDENTITY_Handle *idh;
/**
* Obtain default ego
*/
struct GNUNET_IDENTITY_Operation *get_default;
/**
* Name of the ego controlling the zone.
*/
static char *ego_name;
/**
* Desired action is to add a record.
*/
static int add;
/**
* 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;
/**
* Desired action is to list records.
*/
static int list;
/**
* List iterator for the 'list' operation.
*/
static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
/**
* 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_RECORD)
*/
static int is_shadow;
/**
* Queue entry for the 'del' operation.
*/
static struct GNUNET_NAMESTORE_QueueEntry *del_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;
/**
* Expirationstring converted to relative time.
*/
static struct GNUNET_TIME_Relative etime_rel;
/**
* Expirationstring converted to absolute time.
*/
static struct GNUNET_TIME_Absolute etime_abs;
/**
* 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;
/**
* Task run on shutdown. Cleans up everything.
*
* @param cls unused
*/
static void
do_shutdown (void *cls)
{
(void) cls;
if (NULL != get_default)
{
GNUNET_IDENTITY_cancel (get_default);
get_default = NULL;
}
if (NULL != idh)
{
GNUNET_IDENTITY_disconnect (idh);
idh = NULL;
}
if (NULL != el)
{
GNUNET_IDENTITY_ego_lookup_cancel (el);
el = NULL;
}
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 != 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 != ns)
{
GNUNET_NAMESTORE_disconnect (ns);
ns = NULL;
}
memset (&zone_pkey, 0, sizeof (zone_pkey));
if (NULL != uri)
{
GNUNET_free (uri);
uri = NULL;
}
if (NULL != zm)
{
GNUNET_NAMESTORE_zone_monitor_stop (zm);
zm = NULL;
}
if (NULL != data)
{
GNUNET_free (data);
data = NULL;
}
}
/**
* Check if we are finished, and if so, perform shutdown.
*/
static void
test_finished ()
{
if ( (NULL == add_qe) &&
(NULL == add_qe_uri) &&
(NULL == get_qe) &&
(NULL == del_qe) &&
(NULL == reverse_qe) &&
(NULL == list_it) )
GNUNET_SCHEDULER_shutdown ();
}
/**
* Continuation called to notify client about result of the
* operation.
*
* @param cls closure, location of the QueueEntry pointer to NULL out
* @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
* #GNUNET_NO if content was already there
* #GNUNET_YES (or other positive value) on success
* @param emsg NULL on success, otherwise an error message
*/
static void
add_continuation (void *cls,
int32_t success,
const char *emsg)
{
struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
*qe = NULL;
if (GNUNET_YES != success)
{
fprintf (stderr,
_("Adding record failed: %s\n"),
(GNUNET_NO == success) ? "record exists" : emsg);
if (GNUNET_NO != success)
ret = 1;
}
ret = 0;
test_finished ();
}
/**
* Continuation called to notify client about result of the
* operation.
*
* @param cls closure, unused
* @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
* #GNUNET_NO if content was already there
* #GNUNET_YES (or other positive value) on success
* @param emsg NULL on success, otherwise an error message
*/
static void
del_continuation (void *cls,
int32_t success,
const char *emsg)
{
(void) cls;
del_qe = NULL;
if (GNUNET_NO == success)
{
fprintf (stderr,
_("Deleting record failed, record does not exist%s%s\n"),
(NULL != emsg) ? ": " : "",
(NULL != emsg) ? emsg : "");
}
if (GNUNET_SYSERR == success)
{
fprintf (stderr,
_("Deleting record failed%s%s\n"),
(NULL != emsg) ? ": " : "",
(NULL != emsg) ? emsg : "");
}
test_finished ();
}
/**
* Function called when we are done with a zone iteration.
*/
static void
zone_iteration_finished (void *cls)
{
(void) cls;
list_it = NULL;
test_finished ();
}
/**
* 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;
test_finished ();
}
/**
* 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 char *rname,
unsigned int rd_len,
const struct GNUNET_GNSRECORD_Data *rd)
{
const char *typestring;
char *s;
const char *ets;
struct GNUNET_TIME_Absolute at;
struct GNUNET_TIME_Relative rt;
if ( (NULL != name) &&
(0 != strcmp (name, rname)) )
{
GNUNET_NAMESTORE_zone_iterator_next (list_it,
1);
return;
}
FPRINTF (stdout,
"%s:\n",
rname);
for (unsigned int i=0;idata = data;
rde->data_size = data_size;
rde->record_type = type;
if (1 == is_shadow)
rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
if (1 != is_public)
rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
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
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_EcdsaPrivateKey *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.zkey\n",
reverse_pkey);
else
FPRINTF (stdout,
"%s.gnu\n",
label);
test_finished ();
}
/**
* 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;
test_finished ();
}
/**
* 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_EcdsaPrivateKey *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;
test_finished ();
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