Пример #1
0
/**
 * @brief Restore the peers on disk to #valid_peers.
 */
static void
restore_valid_peers ()
{
  off_t file_size;
  uint32_t num_peers;
  struct GNUNET_DISK_FileHandle *fh;
  char *buf;
  ssize_t size_read;
  char *iter_buf;
  char *str_repr;
  const struct GNUNET_PeerIdentity *peer;

  if (0 == strncmp ("DISABLE", filename_valid_peers, 7))
  {
    return;
  }

  if (GNUNET_OK != GNUNET_DISK_file_test (filename_valid_peers))
  {
    return;
  }
  fh = GNUNET_DISK_file_open (filename_valid_peers,
                              GNUNET_DISK_OPEN_READ,
                              GNUNET_DISK_PERM_NONE);
  GNUNET_assert (NULL != fh);
  GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_handle_size (fh, &file_size));
  num_peers = file_size / 53;
  buf = GNUNET_malloc (file_size);
  size_read = GNUNET_DISK_file_read (fh, buf, file_size);
  GNUNET_assert (size_read == file_size);
  LOG (GNUNET_ERROR_TYPE_DEBUG,
      "Restoring %" PRIu32 " peers from file `%s'\n",
      num_peers,
      filename_valid_peers);
  for (iter_buf = buf; iter_buf < buf + file_size - 1; iter_buf += 53)
  {
    str_repr = GNUNET_strndup (iter_buf, 53);
    peer = s2i_full (str_repr);
    GNUNET_free (str_repr);
    add_valid_peer (peer);
    LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Restored valid peer %s from disk\n",
        GNUNET_i2s_full (peer));
  }
  iter_buf = NULL;
  GNUNET_free (buf);
  LOG (GNUNET_ERROR_TYPE_DEBUG,
      "num_peers: %" PRIu32 ", _size (valid_peers): %u\n",
      num_peers,
      GNUNET_CONTAINER_multipeermap_size (valid_peers));
  if (num_peers != GNUNET_CONTAINER_multipeermap_size (valid_peers))
  {
    LOG (GNUNET_ERROR_TYPE_WARNING,
        "Number of restored peers does not match file size. Have probably duplicates.\n");
  }
  GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
  LOG (GNUNET_ERROR_TYPE_DEBUG,
      "Restored %u valid peers from disk\n",
      GNUNET_CONTAINER_multipeermap_size (valid_peers));
}
Пример #2
0
/**
 * Main function that will be run by the scheduler.
 *
 * @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)
{
  const char *uri;
  const char *slash;
  char *subsystem;
  char *program;
  struct GNUNET_SCHEDULER_Task * rt;

  if (NULL == (uri = args[0]))
  {
    fprintf (stderr,
	     _("No URI specified on command line\n"));
    return;
  }
  if (0 != strncasecmp ("gnunet://", uri, strlen ("gnunet://")))
  {
    fprintf (stderr,
	     _("Invalid URI: does not start with `%s'\n"),
	     "gnunet://");
    return;
  }
  uri += strlen ("gnunet://");
  if (NULL == (slash = strchr (uri, '/')))
  {
    fprintf (stderr, _("Invalid URI: fails to specify subsystem\n"));
    return;
  }
  subsystem = GNUNET_strndup (uri, slash - uri);
  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_string (cfg,
					     "uri",
					     subsystem,
					     &program))
  {
    fprintf (stderr, _("No handler known for subsystem `%s'\n"), subsystem);
    GNUNET_free (subsystem);
    return;
  }
  GNUNET_free (subsystem);
  rt = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
				       GNUNET_DISK_pipe_handle (sigpipe,
								GNUNET_DISK_PIPE_END_READ),
				       &maint_child_death, NULL);
  p = GNUNET_OS_start_process (GNUNET_NO, 0,
			       NULL, NULL, NULL,
			       program,
			       program,
			       args[0],
			       NULL);
  GNUNET_free (program);
  if (NULL == p)
    GNUNET_SCHEDULER_cancel (rt);
}
Пример #3
0
/**
 * Convert the 'value' of a record to a string.
 *
 * @param type type of the record
 * @param data value in binary encoding
 * @param data_size number of bytes in data
 * @return NULL on error, otherwise human-readable representation of the value
 */
char *
GNUNET_NAMESTORE_value_to_string (uint32_t type,
				  const void *data,
				  size_t data_size)
{
  uint16_t mx_pref;
  const struct soa_data *soa;
  const struct vpn_data *vpn;
  const struct srv_data *srv;
  const struct tlsa_data *tlsa;
  struct GNUNET_CRYPTO_ShortHashAsciiEncoded enc;
  struct GNUNET_CRYPTO_HashAsciiEncoded s_peer;
  const char *cdata;
  char* vpn_str;
  char* srv_str;
  char* tlsa_str;
  char* result;
  const char* soa_rname;
  const char* soa_mname;
  char tmp[INET6_ADDRSTRLEN];

  switch (type)
  {
  case 0:
    return NULL;
  case GNUNET_DNSPARSER_TYPE_A:
    if (data_size != sizeof (struct in_addr))
      return NULL;
    if (NULL == inet_ntop (AF_INET, data, tmp, sizeof (tmp)))
      return NULL;
    return GNUNET_strdup (tmp);
  case GNUNET_DNSPARSER_TYPE_NS:
    return GNUNET_strndup (data, data_size);
  case GNUNET_DNSPARSER_TYPE_CNAME:
    return GNUNET_strndup (data, data_size);
  case GNUNET_DNSPARSER_TYPE_SOA:
    if (data_size <= sizeof (struct soa_data))
      return NULL;
    soa = data;
    soa_rname = (const char*) &soa[1];
    soa_mname = memchr (soa_rname, 0, data_size - sizeof (struct soa_data) - 1);
    if (NULL == soa_mname)
      return NULL;
    soa_mname++;
    if (NULL == memchr (soa_mname, 0, 
			data_size - (sizeof (struct soa_data) + strlen (soa_rname) + 1)))
      return NULL;
    GNUNET_asprintf (&result, 
		     "rname=%s mname=%s %lu,%lu,%lu,%lu,%lu",
		     soa_rname, soa_mname,
		     ntohl (soa->serial), 
		     ntohl (soa->refresh),
		     ntohl (soa->retry), 
		     ntohl (soa->expire),
		     ntohl (soa->minimum));
    return result;
  case GNUNET_DNSPARSER_TYPE_PTR:
    return GNUNET_strndup (data, data_size);
  case GNUNET_DNSPARSER_TYPE_MX:
    mx_pref = ntohs(*((uint16_t*)data));
    if (GNUNET_asprintf(&result, "%hu,%s", mx_pref, data+sizeof(uint16_t))
        != 0)
      return result;
    else
    {
      GNUNET_free (result);
      return NULL;
    }
  case GNUNET_DNSPARSER_TYPE_TXT:
    return GNUNET_strndup (data, data_size);
  case GNUNET_DNSPARSER_TYPE_AAAA:
    if (data_size != sizeof (struct in6_addr))
      return NULL;
    if (NULL == inet_ntop (AF_INET6, data, tmp, sizeof (tmp)))
      return NULL;
    return GNUNET_strdup (tmp);
  case GNUNET_NAMESTORE_TYPE_PKEY:
    if (data_size != sizeof (struct GNUNET_CRYPTO_ShortHashCode))
      return NULL;
    GNUNET_CRYPTO_short_hash_to_enc (data,
				     &enc);
    return GNUNET_strdup ((const char*) enc.short_encoding);
  case GNUNET_NAMESTORE_TYPE_PSEU:
    return GNUNET_strndup (data, data_size);
  case GNUNET_NAMESTORE_TYPE_LEHO:
    return GNUNET_strndup (data, data_size);
  case GNUNET_NAMESTORE_TYPE_VPN:
    cdata = data;
    if ( (data_size <= sizeof (struct vpn_data)) ||
	 ('\0' != cdata[data_size - 1]) )
      return NULL; /* malformed */
    vpn = data;
    GNUNET_CRYPTO_hash_to_enc (&vpn->peer, &s_peer);
    if (0 == GNUNET_asprintf (&vpn_str, "%u %s %s",
			      (unsigned int) ntohs (vpn->proto),
			      (const char*) &s_peer,
			      (const char*) &vpn[1]))
    {
      GNUNET_free (vpn_str);
      return NULL;
    }
    return vpn_str;
  case GNUNET_DNSPARSER_TYPE_SRV:
    cdata = data;
    if ( (data_size <= sizeof (struct srv_data)) ||
	 ('\0' != cdata[data_size - 1]) )
      return NULL; /* malformed */
    srv = data;

    if (0 == GNUNET_asprintf (&srv_str, 
			      "%d %d %d %s",
			      ntohs (srv->prio),
			      ntohs (srv->weight),
			      ntohs (srv->port),
			      (const char *)&srv[1]))
    {
      GNUNET_free (srv_str);
      return NULL;
    }
    return srv_str;
  case GNUNET_DNSPARSER_TYPE_TLSA:
    cdata = data;
    if ( (data_size <= sizeof (struct tlsa_data)) ||
	 ('\0' != cdata[data_size - 1]) )
      return NULL; /* malformed */
    tlsa = data;
    if (0 == GNUNET_asprintf (&tlsa_str, 
			      "%c %c %c %s",
			      tlsa->usage,
			      tlsa->selector,
			      tlsa->matching_type,
			      (const char *) &tlsa[1]))
    {
      GNUNET_free (tlsa_str);
      return NULL;
    }
    return tlsa_str;
  default:
    GNUNET_break (0);
  }
  GNUNET_break (0); // not implemented
  return NULL;
}
Пример #4
0
/**
 * Convert the 'value' of a record to a string.
 *
 * @param cls closure, unused
 * @param type type of the record
 * @param data value in binary encoding
 * @param data_size number of bytes in @a data
 * @return NULL on error, otherwise human-readable representation of the value
 */
static char *
gns_value_to_string (void *cls,
                     uint32_t type,
                     const void *data,
                     size_t data_size)
{
  const char *cdata;

  switch (type)
  {
  case GNUNET_GNSRECORD_TYPE_PKEY:
    if (data_size != sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))
      return NULL;
    return GNUNET_CRYPTO_ecdsa_public_key_to_string (data);
  case GNUNET_GNSRECORD_TYPE_NICK:
    return GNUNET_strndup (data, data_size);
  case GNUNET_GNSRECORD_TYPE_LEHO:
    return GNUNET_strndup (data, data_size);
  case GNUNET_GNSRECORD_TYPE_GNS2DNS:
    {
      char *ns;
      char *ip;
      size_t off;
      char *nstr;

      off = 0;
      ns = GNUNET_DNSPARSER_parse_name (data,
					data_size,
					&off);
      ip = GNUNET_DNSPARSER_parse_name (data,
					data_size,
					&off);
      if ( (NULL == ns) ||
           (NULL == ip) ||
	   (off != data_size) )
      {
	GNUNET_break_op (0);
	GNUNET_free_non_null (ns);
	GNUNET_free_non_null (ip);
	return NULL;
      }
      GNUNET_asprintf (&nstr,
                       "%s@%s",
                       ns,
                       ip);
      GNUNET_free_non_null (ns);
      GNUNET_free_non_null (ip);
      return nstr;
    }
  case GNUNET_GNSRECORD_TYPE_VPN:
    {
      const struct GNUNET_TUN_GnsVpnRecord *vpn;
      char* vpn_str;

      cdata = data;
      if ( (data_size <= sizeof (struct GNUNET_TUN_GnsVpnRecord)) ||
	   ('\0' != cdata[data_size - 1]) )
	return NULL; /* malformed */
      vpn = data;
      GNUNET_asprintf (&vpn_str,
                       "%u %s %s",
                       (unsigned int) ntohs (vpn->proto),
                       (const char*) GNUNET_i2s_full (&vpn->peer),
                       (const char*) &vpn[1]);
      return vpn_str;
    }
  case GNUNET_GNSRECORD_TYPE_BOX:
    {
      const struct GNUNET_GNSRECORD_BoxRecord *box;
      uint32_t rt;
      char *box_str;
      char *ival;

      if (data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
	return NULL; /* malformed */
      box = data;
      rt = ntohl (box->record_type);
      ival = GNUNET_GNSRECORD_value_to_string (rt,
                                               &box[1],
                                               data_size - sizeof (struct GNUNET_GNSRECORD_BoxRecord));
      if (NULL == ival)
        return NULL; /* malformed */
      GNUNET_asprintf (&box_str,
                       "%u %u %u %s",
                       (unsigned int) ntohs (box->protocol),
                       (unsigned int) ntohs (box->service),
                       (unsigned int) rt,
                       ival);
      GNUNET_free (ival);
      return box_str;
    }
  default:
    return NULL;
  }
}
Пример #5
0
/**
 * Main request handler.
 */
static int
access_handler_callback (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 **con_cls)
{
  static int dummy;
  static const struct Entry map[] = {
    { "prefix", "prefix" },
    { "name", "name" },
    { "suffix", "suffix" },
    { "street", "street" },
    { "city", "city" },
    { "phone", "phone" },
    { "fax", "fax" },
    { "email", "email"},
    { "homepage", "homepage" },
    { "orga", "orga"},
    { "departmenti18n", "departmentde"},
    { "departmenten", "departmenten"},
    { "subdepartmenti18n", "subdepartmentde"},
    { "subdepartmenten", "subdepartmenten"},
    { "jobtitlei18n", "jobtitlegerman"},
    { "jobtitleen", "jobtitleenglish"},
    { "subdepartmenten", "subdepartmenten"},
    { NULL, NULL }
  };

  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _("Refusing `%s' request to HTTP server\n"),
                method);
    return MHD_NO;
  }
  if (NULL == *con_cls)
  {
    (*con_cls) = &dummy;
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Sending 100 CONTINUE reply\n");
    return MHD_YES;             /* send 100 continue */
  }
  if (0 == strcasecmp (url, "/"))
    return MHD_queue_response (connection,
                               MHD_HTTP_OK,
                               main_response);
  if (0 == strcasecmp (url, "/submit.pdf"))
  {
    unsigned int i;
    char *p;
    char *tmp;
    char *deffile;
    struct GNUNET_CRYPTO_EcdsaPublicKey pub;
    size_t slen;
    FILE *f;
    struct stat st;
    struct MHD_Response *response;
    int fd;
    int ret;

    const char *gpg_fp = MHD_lookup_connection_value (connection,
                                                      MHD_GET_ARGUMENT_KIND,
                                                      "gpgfingerprint");
    const char *gns_nick = MHD_lookup_connection_value (connection,
                                                        MHD_GET_ARGUMENT_KIND,
                                                        "gnsnick");
    const char *gnskey = MHD_lookup_connection_value (connection,
                                                      MHD_GET_ARGUMENT_KIND,
                                                      "gnskey");
    if ( (NULL == gnskey) ||
         (GNUNET_OK !=
          GNUNET_CRYPTO_ecdsa_public_key_from_string (gnskey,
                                                      strlen (gnskey),
                                                      &pub)))
    {
      return MHD_queue_response (connection,
                                 MHD_HTTP_OK,
                                 invalid_gnskey_response);
    }
    tmp = GNUNET_DISK_mkdtemp (gnskey);
    if (NULL == tmp)
    {
      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mktemp", gnskey);
      return MHD_NO;
    }
    GNUNET_asprintf (&deffile,
                     "%s%s%s",
                     tmp, DIR_SEPARATOR_STR, "def.tex");
    f = FOPEN (deffile, "w");
    if (NULL == f)
    {
      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", deffile);
      GNUNET_free (deffile);
      GNUNET_DISK_directory_remove (tmp);
      GNUNET_free (tmp);
      return MHD_NO;
    }
    for (i=0; NULL != map[i].formname; i++)
    {
      const char *val =  MHD_lookup_connection_value (connection,
                                                      MHD_GET_ARGUMENT_KIND,
                                                      map[i].formname);
      if (NULL != val)
        FPRINTF (f,
                 "\\def\\%s{%s}\n",
                 map[i].texname, val);
      else
        FPRINTF (f,
                 "\\def\\%s{}\n",
                 map[i].texname);
    }
    if (NULL != gpg_fp)
    {
      char *gpg1;
      char *gpg2;

      slen = strlen (gpg_fp);
      gpg1 = GNUNET_strndup (gpg_fp, slen / 2);
      gpg2 = GNUNET_strdup (&gpg_fp[slen / 2]);
      FPRINTF (f,
               "\\def\\gpglineone{%s}\n\\def\\gpglinetwo{%s}\n",
               gpg1, gpg2);
      GNUNET_free (gpg2);
      GNUNET_free (gpg1);
    }
    FPRINTF (f,
             "\\def\\gns{%s/%s}\n",
             gnskey,
             (NULL == gns_nick) ? "" : gns_nick);
    FCLOSE (f);
    GNUNET_asprintf (&p,
                     "cd %s; cp %s gns-bcd.tex | pdflatex --enable-write18 gns-bcd.tex > /dev/null 2> /dev/null",
                     tmp,
                     resfile);
    GNUNET_free (deffile);
    ret = system (p);
    if (WIFSIGNALED (ret) || (0 != WEXITSTATUS(ret)))
      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
                                "system",
                                p);
    GNUNET_asprintf (&deffile,
                     "%s%s%s",
                     tmp, DIR_SEPARATOR_STR, "gns-bcd.pdf");
    fd = OPEN (deffile, O_RDONLY);
    if (-1 == fd)
    {
      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
                                "open",
                                deffile);
      GNUNET_free (deffile);
      GNUNET_free (p);
      GNUNET_DISK_directory_remove (tmp);
      GNUNET_free (tmp);
      return MHD_NO;
    }
    GNUNET_break (0 == STAT (deffile, &st));
    if (NULL == (response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
    {
      GNUNET_break (0);
      GNUNET_break (0 == CLOSE (fd));
      GNUNET_free (deffile);
      GNUNET_free (p);
      GNUNET_DISK_directory_remove (tmp);
      GNUNET_free (tmp);
      return MHD_NO;
    }
    (void) MHD_add_response_header (response,
                                    MHD_HTTP_HEADER_CONTENT_TYPE,
                                    "application/pdf");
    ret = MHD_queue_response (connection,
                              MHD_HTTP_OK,
                              response);
    MHD_destroy_response (response);
    GNUNET_free (deffile);
    GNUNET_free (p);
    GNUNET_DISK_directory_remove (tmp);
    GNUNET_free (tmp);
    return ret;
  }
  return MHD_queue_response (connection,
                             MHD_HTTP_NOT_FOUND,
                             not_found_response);
}
Пример #6
0
/**
 * We're building a HELLO.  Parse the next address from the
 * parsing context and append it.
 *
 * @param cls the 'struct GNUNET_PEERINFO_HelloAddressParsingContext'
 * @param max number of bytes available for HELLO construction
 * @param buffer where to copy the next address (in binary format)
 * @return number of bytes added to buffer
 */ 
static size_t
add_address_to_hello (void *cls, size_t max, void *buffer)
{
  struct GNUNET_PEERINFO_HelloAddressParsingContext *ctx = cls;
  const char *tname;
  const char *address;
  char *uri_address;
  char *plugin_address;
  const char *end;
  char *plugin_name;
  struct tm expiration_time;
  time_t expiration_seconds;
  struct GNUNET_TIME_Absolute expire;
  struct GNUNET_TRANSPORT_PluginFunctions *papi;
  void *addr;
  size_t addr_len;
  struct GNUNET_HELLO_Address haddr;
  size_t ret;

  if (NULL == ctx->pos)
    return 0;
  if ('!' != ctx->pos[0])
  {
    ctx->ret = GNUNET_SYSERR;
    GNUNET_break (0);
    return 0;
  }
  ctx->pos++;
  memset (&expiration_time, 0, sizeof (expiration_time));
  tname = strptime (ctx->pos,
		    "%Y%m%d%H%M%S",
		    &expiration_time);

  if (NULL == tname)
  {
    ctx->ret = GNUNET_SYSERR;
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Failed to parse HELLO message: missing expiration time\n"));
    GNUNET_break (0);
    return 0;
  }
  expiration_seconds = mktime (&expiration_time);
  if (expiration_seconds == (time_t) -1)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Failed to parse HELLO message: invalid expiration time\n"));
    ctx->ret = GNUNET_SYSERR;
    GNUNET_break (0);
    return 0;
  }
  expire.abs_value = expiration_seconds * 1000;
  if ('!' != tname[0])
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Failed to parse HELLO message: malformed\n"));
    ctx->ret = GNUNET_SYSERR;
    GNUNET_break (0);
    return 0;
  }
  tname++;
  address = strchr (tname, (int) '!');
  if (NULL == address)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Failed to parse HELLO message: missing transport plugin\n"));
    ctx->ret = GNUNET_SYSERR;
    GNUNET_break (0);
    return 0;
  }
  address++;
  end = strchr (address, (int) '!');
  ctx->pos = end;
  plugin_name = GNUNET_strndup (tname, address - (tname+1));
  papi = GPI_plugins_find (plugin_name);
  if (NULL == papi)
  {
    /* Not an error - we might just not have the right plugin.
     * Skip this part, advance to the next one and recurse.
     * But only if this is not the end of string.
     */
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Plugin `%s' not found\n"),
                plugin_name);
    GNUNET_free (plugin_name);
    GNUNET_break (0);
    return 0;
  }
  if (NULL == papi->string_to_address)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
		_("Plugin `%s' does not support URIs yet\n"),
		plugin_name);
    GNUNET_free (plugin_name);
    GNUNET_break (0);
    return 0;
  }
  uri_address = GNUNET_strndup (address, end - address);
  /* For URIs we use '(' and ')' instead of '[' and ']' as brackets are reserved
     characters in URIs; need to convert back to '[]' for the plugin */
   plugin_address = map_characters (uri_address, "()", "[]");
  GNUNET_free (uri_address);
  if (GNUNET_OK !=
      papi->string_to_address (papi->cls, 
			       plugin_address,
			       strlen (plugin_address) + 1,
			       &addr,
			       &addr_len))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Failed to parse `%s' as an address for plugin `%s'\n"),
		plugin_address,
		plugin_name);
    GNUNET_free (plugin_name);
    GNUNET_free (plugin_address);
    return 0;
  }
  GNUNET_free (plugin_address);
  /* address.peer is unset - not used by add_address() */
  haddr.address_length = addr_len;
  haddr.address = addr;
  haddr.transport_name = plugin_name;
  ret = GNUNET_HELLO_add_address (&haddr, expire, buffer, max);
  GNUNET_free (addr);
  GNUNET_free (plugin_name);
  return ret;
}
Пример #7
0
/**
 * Convert the 'value' of a record to a string.
 *
 * @param cls closure, unused
 * @param type type of the record
 * @param data value in binary encoding
 * @param data_size number of bytes in @a data
 * @return NULL on error, otherwise human-readable representation of the value
 */
static char *
gns_value_to_string (void *cls,
                     uint32_t type,
                     const void *data,
                     size_t data_size)
{
  const char *cdata;

  switch (type)
  {
  case GNUNET_GNSRECORD_TYPE_PKEY:
    if (data_size != sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))
      return NULL;
    return GNUNET_CRYPTO_ecdsa_public_key_to_string (data);
  case GNUNET_GNSRECORD_TYPE_NICK:
    return GNUNET_strndup (data, data_size);
  case GNUNET_GNSRECORD_TYPE_LEHO:
    return GNUNET_strndup (data, data_size);
  case GNUNET_GNSRECORD_TYPE_GNS2DNS:
    {
      char *ns;
      char *ip;
      size_t off;
      char *nstr;

      off = 0;
      ns = GNUNET_DNSPARSER_parse_name (data,
					data_size,
					&off);
      ip = GNUNET_DNSPARSER_parse_name (data,
					data_size,
					&off);
      if ( (NULL == ns) ||
           (NULL == ip) ||
	   (off != data_size) )
      {
	GNUNET_break_op (0);
	GNUNET_free_non_null (ns);
	GNUNET_free_non_null (ip);
	return NULL;
      }
      GNUNET_asprintf (&nstr,
                       "%s@%s",
                       ns,
                       ip);
      GNUNET_free_non_null (ns);
      GNUNET_free_non_null (ip);
      return nstr;
    }
  case GNUNET_GNSRECORD_TYPE_VPN:
    {
      struct GNUNET_TUN_GnsVpnRecord vpn;
      char* vpn_str;

      cdata = data;
      if ( (data_size <= sizeof (vpn)) ||
	   ('\0' != cdata[data_size - 1]) )
	return NULL; /* malformed */
      /* need to memcpy for alignment */
      memcpy (&vpn,
              data,
              sizeof (vpn));
      GNUNET_asprintf (&vpn_str,
                       "%u %s %s",
                       (unsigned int) ntohs (vpn.proto),
                       (const char*) GNUNET_i2s_full (&vpn.peer),
                       (const char*) &cdata[sizeof (vpn)]);
      return vpn_str;
    }
  case GNUNET_GNSRECORD_TYPE_BOX:
    {
      struct GNUNET_GNSRECORD_BoxRecord box;
      uint32_t rt;
      char *box_str;
      char *ival;

      cdata = data;
      if (data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
	return NULL; /* malformed */
      memcpy (&box,
              data,
              sizeof (box));
      rt = ntohl (box.record_type);
      ival = GNUNET_GNSRECORD_value_to_string (rt,
                                               &cdata[sizeof (box)],
                                               data_size - sizeof (box));
      if (NULL == ival)
        return NULL; /* malformed */
      GNUNET_asprintf (&box_str,
                       "%u %u %u %s",
                       (unsigned int) ntohs (box.protocol),
                       (unsigned int) ntohs (box.service),
                       (unsigned int) rt,
                       ival);
      GNUNET_free (ival);
      return box_str;
    }
  case GNUNET_GNSRECORD_TYPE_REVERSE:
    {
      struct GNUNET_GNSRECORD_ReverseRecord rev;
      char *rev_str;
      char *pkey_str;

      if (data_size < sizeof (struct GNUNET_GNSRECORD_ReverseRecord))
        return NULL; /* malformed */

      memcpy (&rev,
              data,
              sizeof (rev));
      cdata = data;
      pkey_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&rev.pkey);

      GNUNET_asprintf (&rev_str,
                       "%s %s %"SCNu64,
                       &cdata[sizeof (rev)],
                       pkey_str,
                       rev.expiration.abs_value_us);
      GNUNET_free (pkey_str);
      return rev_str;

    }
  default:
    return NULL;
  }
}